模組間呼叫
#在一個應用系統中,無論使用何種語言開發,必然存在著模組之間的調用,調用的方式分為幾種:
(1)同步調用
#同步調用是最基本且最簡單的一種呼叫方式,類別A的方法a()呼叫類別B的方法b(),一直等待b()方法執行完畢,a()方法繼續往下走。 這種呼叫方式適用於方法b()執行時間不長的情況,因為b()方法執行時間一長或直接阻塞的話,a()方法的餘下程式碼是無法執行下去的,這樣會造成整個流程的阻塞。
(2)非同步呼叫
非同步呼叫是為了解決同步呼叫可能會出現阻塞,導致整個流程卡住而產生的一種呼叫方式。 類別A的方法方法a()透過新起執行緒的方式呼叫類別B的方法b(),程式碼接著直接往下執行#,這樣無論方法b()執行時間多久,都不會阻塞住方法a()的執行。
但是這種方式,由於方法a()不等待方法b()的執行完成,在方法a()需要方法b()執行結果的情況下(視具體業務而定,有些業務例如啟非同步執行緒發個微信通知、刷新一個快取這種就沒必要),必須透過一定的方式對方法b()的執行結果進行監聽。
(3)回呼
#最後是回呼,回呼的想法是:
這樣一種呼叫方式組成了上圖,也就是一種雙向的呼叫方式。
程式碼範例
#接下來看一下回呼的程式碼範例,程式碼模擬的是這樣一個場景:老師問學生問題,學生思考完畢回答老師。
先定義一個回呼接口,只有一個方法tellAnswer(int answer),也就是學生思考完畢告訴老師答案:
/** * 回调接口,原文出处http://www.cnblogs.com/xrq730/p/6424471.html */ public interface Callback { public void tellAnswer(int answer); }
定義一個老師對象,實作Callback介面:
/** * 老师对象,原文出处http://www.cnblogs.com/xrq730/p/6424471.html */ public class Teacher implements Callback { private Student student; public Teacher(Student student) { this.student = student; } public void askQuestion() { student.resolveQuestion(this); } @Override public void tellAnswer(int answer) { System.out.println("知道了,你的答案是" + answer); } }
老師對像有兩個public方法:
(1)回呼介面tellAnswer(int answer),也就是學生回答完畢問題之後,老師要做的事情
(2)問問題方法askQuestion(),即向學生問問題
接著定義一個學生接口,學生當然是解決問題,但是接收一個Callback參數,這樣學生就知道解決完畢問題向誰報告:
/** * 学生接口,原文出处http://www.cnblogs.com/xrq730/p/6424471.html */ public interface Student { public void resolveQuestion(Callback callback); }
最後定義一個具體的學生叫Ricky:
/** * 一个名叫Ricky的同学解决老师提出的问题,原文出处http://www.cnblogs.com/xrq730/p/6424471.html */ public class Ricky implements Student { @Override public void resolveQuestion(Callback callback) { // 模拟解决问题 try { Thread.sleep(3000); } catch (InterruptedException e) { } // 回调,告诉老师作业写了多久 callback.tellAnswer(3); } }
在解決完畢問題之後,第16行向老師報告答案。
寫一個測試類,比較簡單:
/** * 回调测试,原文出处http://www.cnblogs.com/xrq730/p/6424471.html */ public class CallbackTest { @Test public void testCallback() { Student student = new Ricky(); Teacher teacher = new Teacher(student); teacher.askQuestion(); } }
程式碼運行結果就一行:
<span style="color: #000000;">知道了,你的答案是3<br></span>
簡單總結、分析這個例子是:
(1)老師呼叫學生介面的方法resolveQuestion,向學生提問
(2)學生解決完畢問題之後呼叫老師的回呼方法tellAnswer
這樣一套流程,構成了一種雙向調用的關係。
程式碼分析
分析上面的程式碼,上面的程式碼我這裡做了兩層的抽象:
# (1)將老師進行抽象
(2)將學生進行抽象
這個例子是一個典型的體現接口作用的例子,之所以這麼說是因為我想到有些朋友可能不太明白接口的好處,不太明白接口好處的朋友可以重點看一下這個例子,多多理解。
總結起來,回呼的核心就是回呼方將本身即this傳遞給呼叫方,這樣呼叫方就可以在調用完畢之後告訴回調方它想要知道的資訊。回調是一種想法、是一種機制,至於具體如何實現,如何透過程式碼將回呼實現得優雅、實現得可擴展性比較高,一看開發者的個人水平,二看開發者對業務的理解程度。
同步回呼與非同步回呼
#上面的例子,可能有人會提出這樣的疑問:
這個例子需要用什麼回呼啊,使用同步呼叫的方式,學生對象回答完畢問題之後直接把回答的答案回傳給老師對像不就好了?
這個問題的提出沒有任何問題,可以從兩個角度去理解這個問題。
首先,老師不只是想要得到學生的答案怎麼辦?
可能這個老師是個更喜歡聽學生解題思路的老師,在得到學生的答案之前,老師更想先知道學生姓名和學生的解題思路,當然有些人可以說,那我可以定義一個對象,裡面加上學生的姓名和解題思路不就好了。這個說法在我看來有兩個問題:
(1)如果老師想要的資料越來越多,那麼傳回的物件得越來越大,而使用回呼則可以進行資料分離,將一批資料放在回調方法中處理,至於哪些資料依具體業務而定,如果需要增加返回參數,直接在回調方法中增加即可
(2)無法解決老師希望得到學生姓名、學生解題思路先於學生回答的答案的問題
因此我認為簡單的返回某個結果確實沒有必要使用回調而可以直接使用同步調用,但是如果有多種數據需要處理且資料有主次之分,使用回呼會是一種更合適的選擇,優先處理的資料放在回呼方法中先處理掉。
另外一個理解的角度則更加重要,就是標題說的同步回調和非同步回調了。例子是同步回調的例子,意思是老師問Ricky問題,Ricky給答案,老師問下一個同學,得到答案之後繼續問下一個同學,這是正常的場景,但是如果我把場景改一下:
老師並不想One-By-One這樣提問,而是同時向Ricky、Mike、Lucy、Bruce、Kate五位同學提問,讓同學自己思考,哪位同學思考好了就直接告訴老師答案即可。
這種場景相當於是說,同學思考完畢完畢問題要有一個辦法告訴老師,有兩個解決方案:
(1)使用Future Callable的方式,等待非同步執行緒執行結果,這相當於就是同步呼叫的一種變種,因為其本質還是方法返回一個結果,即學生的回答
(2)使用非同步回調,同學回答完畢問題,呼叫回調介面方法告訴老師答案即可。由於老師物件被抽象化成了Callback接口,因此這種做法的擴展性非常好,就像之前說的,即使老師換了換了一茬又一茬,對於同學來說,只關心的是調用Callback接口回傳必要的資訊即可
更多相關知識請關注java基礎教學欄位
以上是Java回呼機制介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!