1. 程式人生 > 程式設計 >Java中的回撥

Java中的回撥

  又忙了一週,事情差不多解決了,終於有可以繼續寫我的部落格了(各位看官久等了)。

  這次我們來談一談Java裡的一個很有意思的東西——回撥。

  什麼叫回調,一本正經的來講,在計算機程式設計中,回撥函式是指通過函式引數傳遞到其它程式碼的,某一塊可執行程式碼的引用。這一設計允許了底層程式碼呼叫在高層定義的子程式。

  別急別急,且聽我慢慢道來。

  舉個栗子,設定這樣一個情景,老闆安排員工做事,然後讓他做完後跟他電話說一聲。老闆當然不會在那裡一直等員工做完事情才去做其他事,而是隻交代完任務就去忙自己的事情了。

  這個例子包含了非同步+回撥的思想,員工做完任務後向老闆報告這個過程,就叫回調,當然,報告的話,老闆肯定先跟員工說好了報告方式,比如說郵件,電話等,而交代報告方式,就是註冊回撥函式,這裡的回撥函式必須符合介面的規範。

  好像還是有些不明白?來上程式碼吧。

  先定義一個介面:.

public interface ReceiveReport {
  /**
   * 接收報告
   * @param name 員工名稱
   * @param report 報告內容
   */
  public void receiveReport(String name,String report);
}

  定義一個Boss類實現這個接收報告的介面:

public class Boss implements ReceiveReport{
  private Worker worker;

  public Boss(Worker worker){
    this.worker = worker;
  }

  /**
   * 下達任務
   */
  public void sendTask(){
    worker.work(this);
  }

  /**
   * 接收報告
   * @param name 員工名稱
   * @param report 報告內容
   */
  public void receiveReport(String name,String report){
    System.out.println("收到:"+name+" 的報告:"+report);
  }
}

  定義一個Worker介面:

public interface Worker {
  public void work(ReceiveReport boss);
}

  定義一個員工類。

public class Employee implements Worker{
  private String name;//員工姓名

  //構造器
  public Employee(String name) {
    this.name = name;
  }

  /**
   * 工作
   * @param boss 任務名稱
   */
  public void work(ReceiveReport boss){
    System.out.println(name + " is doing works.");
    String report = "我已經完成了任務!";
    boss.receiveReport(name,report);
  }
}

  然後來測試一下:

public class Test {
  public static void main(String[] args) {
    Worker employee = new Employee("Frank");//定義一個員工
    Boss boss = new Boss(employee);//定義一個Boss
    //boss開始下達任務
    boss.sendTask();
  }
}

  測試結果:

Frank is doing works.收到:Frank 的報告:我已經完成了任務!

  至此,員工與老闆的互動就完成了,這就是一個簡單的同步回調了。Boss通過Worker介面可以給員工安排工作,而不用去關心是哪個員工在工作,Worker通過ReceiveReport來向Boss報告工作情況,兩個類通過介面進行回撥互動,可以很好的解耦合,因為Boss可以安排不同的員工,只要他們實現了Worker介面就行,而員工也可以向不同的boss彙報情況,只要實現了ReceiveReport介面即可。

其實回撥的核心思想就是把自身的this指標傳給呼叫方,就像這裡把employee傳入Boss類中,在work方法中又註冊了回撥,於是兩者的互動性就很強了。

  那麼為什麼要用回撥呢?如果Boss要在員工完成工作之前登記員工的一些資訊,如姓名等,那麼有了回撥機制,通過把this指標傳入,就能在Boss內部為所欲為了,而不需要通過設計新的方法來獲取,而且需要獲得的資料越多,回撥的優勢越明顯。

  其實這裡只是簡單的一對一關係,如果是一個Boss,多個員工,那就是簡單的觀察者模式,如果是多個Boss多個員工,那就是簡單生產者-消費者模式了。

  當然,這裡僅僅是簡單的同步回撥。員工只能一個接一個的去完成任務,也就是說前一個員工必須等待後一個員工完成任務後才能開始任務,事實上,員工一般是同時進行工作的。

  如果換一個場景,現在有十個員工,老闆釋出任務,前三名完成的人有獎金獎勵,那麼就需要用到非同步回調了,sendTask的時候使用執行緒即可,我們來修改一下程式碼:

/**
 * @author Frank
 * @create 2017/12/3
 * @description 接收報告介面
 */
public interface ReceiveReport {
  /**
   * 接收報告
   * @param worker 員工
   * @param report 報告內容
   */
  public void receiveReport(Worker worker,String report);
}
/**
 * @author Frank
 * @create 2017/12/3
 * @description 工人介面
 */
public interface Worker {
  public void work(String taskName);
  public void setReceiveReport(ReceiveReport boss);
  public void getReward(Double money);
  public String getName();
}
import java.util.Random;

/**
 * @author Frank
 * @create 2017/12/3
 * @description 員工類
 */
public class Employee implements Worker{
  private ReceiveReport boss;
  private String name;//員工姓名

  @Override
  public String getName() {
    return name;
  }

  //構造器
  public Employee(String name) {
    this.name = name;
  }

  public void setReceiveReport(ReceiveReport boss) {
    this.boss = boss;
  }

  @Override
  public void getReward(Double money) {
    System.out.println(name+"由於表現突出,獲得$"+money+"現金獎勵!");
  }

  /**
   * 工作
   * @param taskName 任務名稱
   */
  public void work(String taskName){
    System.out.println(name + " is doing works:"+taskName);
    Random random = new Random();
    Integer time = random.nextInt(10000);
    try {
      Thread.sleep(time);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    String report = "順利完成任務!";
    //通知老闆
    boss.receiveReport(this,report);
  }
}
import java.util.ArrayList;
import java.util.List;

/**
 * @author Frank
 * @create 2017/12/3
 * @description Boss類
 */
public class Boss implements ReceiveReport{
  private List<Worker> workers = new ArrayList<>();//老闆管理的員工
  private volatile int index;//順序

  /**
   * 新增員工
   * @param worker 員工
   */
  public void addWorker(Worker worker){
    workers.add(worker);
    worker.setReceiveReport(this);
  }

  /**
   * 下達任務
   */
  public void sendTask(String task){
    //給各個員工依次下達任務
    for (Worker w:workers){
      new Thread(new Runnable() {
        @Override
        public void run() {
          w.work(task);
        }
      }).start();
    }
  }

  /**
   * 接收報告
   * @param worker 員工
   * @param report 報告內容
   */
  public void receiveReport(Worker worker,String report){
    int index = ++this.index;
    System.out.println(worker.getName()+"獲得第"+index+"名");
    if (index <= 3){
      //給前三名發獎金
      worker.getReward(1000.0*(4-index));
    }
  }
}
/**
 * @author Frank
 * @create 2017/12/3
 * @description
 */
public class Test {
  public static void main(String[] args) {
    Boss boss = new Boss();//定義一個Boss
    //定義十個員工
    for (int i=0;i<10;i++){
      Worker worker = new Employee("Employee["+i+"]");
      boss.addWorker(worker);
    }
    //boss開始下達任務
    boss.sendTask("Say Hello");

  }
}

  這裡沒有使用鎖,因為設定的時間間隔區間為0-10s,發生併發衝突的概率很低,而且由於現在還沒有說多執行緒的內容,所以暫時先不使用。只需要知道在sendTask方法中,依次啟動了執行緒來呼叫每個Worker的work方法,執行緒啟動後會同時執行,執行完畢後,又會呼叫Boss的receiveReport方法來向Boss反饋結果,接收結果後,根據完成順序,再呼叫Worker的getReward方法來給前三名發獎金。其實這裡是雙向回調了,Boss把this指標傳給了Worker,Worker又把自己的this指標傳給了Worker。

  程式執行結果如下:

Employee[0] is doing works:Say Hello
Employee[4] is doing works:Say Hello
Employee[3] is doing works:Say Hello
Employee[2] is doing works:Say Hello
Employee[1] is doing works:Say Hello
Employee[5] is doing works:Say Hello
Employee[7] is doing works:Say Hello
Employee[6] is doing works:Say Hello
Employee[9] is doing works:Say Hello
Employee[8] is doing works:Say Hello
Employee[9]獲得第1名
Employee[9]由於表現突出,獲得$3000.0現金獎勵!
Employee[7]獲得第2名
Employee[7]由於表現突出,獲得$2000.0現金獎勵!
Employee[3]獲得第3名
Employee[3]由於表現突出,獲得$1000.0現金獎勵!
Employee[1]獲得第4名
Employee[0]獲得第5名
Employee[5]獲得第6名
Employee[4]獲得第7名
Employee[8]獲得第8名
Employee[6]獲得第9名
Employee[2]獲得第10名

  因為使用了多執行緒,所以每次執行的結果可能都會不一樣,如果得到了不一樣的結果,那是很正常的現象。

  舉了這兩個栗子,對回撥應該也有了一定的瞭解了吧。

  其實回撥只是一種思想,並不是java中獨有的內容,思想這種東西,是為了解決特定場景下的特定問題而出現的,只有被正確應用了才有它的價值,而不要為了使用它而使用它。

  至此,回撥講解完畢,如有說明有誤的地方,歡迎各位批評指正。也歡迎大家繼續關注。

以上就是Java中的回撥的詳細內容,更多關於Java 回撥的資料請關注我們其它相關文章!