來自:碼農翻身(微信號:coderising)
作者:王欽譽
前言:本文原作者是王欽譽,原文鏈接:https://xiaoqinyu0000.github.io/Java/JavaCallback/ (點擊尾部閱讀原文前往)
文章主要介紹Java的回調機制及其實現。
故事背景
在日常編程中,我們經常需要對內存的數據進行持久化的工作,把他們保存在硬盤文件或者數據庫中。
爲了避免重複, 我們通常會把這部分工作封裝在一個工具類中, 讓各個客戶端來調用。
下文的FileIO就是一個簡單的工具類(爲了簡單起見,並沒有使用單例或靜態方法來實現)
小張的煩惱
Java 帝國的FileIO是一個忙碌的傢伙,附近7、8個村落的人都來找他, 請他把數據存儲到硬盤裏。
FileIO提供了一個簡單的接口, 大家只要告訴他文件名和要保存的字符串內容, 剩下的事就只是等待了,FileIO會完成工作,告訴大家是成功還是失敗。
比如張家村的小張來請FileIO保存文件的時候是這樣的:
通常情況下, 小張都會很快拿到返回結果, 高高興興的回家。
但這一次不知道怎麼回事, 這個FileIO一直不返回結果, 把小張阻塞了長達1秒鐘!
小張說: “哥們, 怎麼回事? 我這兒都等了1000毫秒了, 還沒完?
FileIO回答 : ”這不能怪我啊, 你這次的數據量實在是太大了,是誰上傳的大文件故意搗亂吧, 對了, 你殺毒沒有?“
"安全問題不用你考慮 " 小張也有點底氣不足 :”我覺得數據量還行, 也有可能是硬盤這會兒太忙了”
總之,小張一直阻塞在那裏,無法回家。
回調
阻塞的事情發生的多了,極大的影響了小張的工作, 最近這一週的工分可是落後了不少啊, 再這麼下去,月底分糧的時候就要餓肚子了, 餓肚子還是小事, 自己喜歡的張二妮看到沒糧食,估計就不理我了。。。
他拎了兩瓶好酒去找FileIO商量: “兄弟, 我聽說有一種異步保存的辦法, 你那邊能不能用下? 保存數據的時候起一個線程, 把主線程讓回給我,保存好了再通知我,我也不用老是等你,是吧?”
FIleIO想了想說:“這樣確實可以解決問題,但每天找我保存數據的人也很多,而且我也不知道在完成數據的寫入之後怎麼通知你呢?”
小張把兩瓶好酒往前一推, “我們關係這麼好,你再開個專屬我的方法唄,我在調用你的saveStrToFile方法的時候順便把我的實例給你,你搞完之後通過我的實例調用我的方法通知我就行啦。就調我的onResult()這個方法吧。這事要保密, 天知地知你知我知就行了”。
於是,FileIO爲小張開了一個VIP通道:
這種方式很巧妙,小張調用FileIO的saveStrToFile(String,String,XiaoZhang)的時候,把自己的實例通過第三個參數給了FileIO,FileIO開啓子線程保存完數據之後,通過XiaoZhang給的實例回調onResult(boolean)方法。
聽起來很繞口,但總結起來就我調你的方法,你再回調我的方法。
後來,JAVA帝國給這種機制取了個名字叫回調機制,在帝國中廣爲人知。
酒後泄密
由於有了FileIO的VIP通道,小張處理業務的能力大幅度提升, 工分不但在張家村獨佔鰲頭, 就是算上李家村, 劉家村 等,那也是數一數二的。
小張一時風光不已,越來越多的人來向他請教祕訣,但小張卻笑而不語(這可是成功祕訣,能告訴你們嘛...)。
有一次, 李家村的小李看到了FileIO有了一個新接口(畢竟都是公開的嘛), 但是不知道怎麼回事, 自己也調用不了, 類型不對啊。
小李別有用心的請小張和FileIO喝酒, 酒過三巡, 倆人終於吐露了這個祕密。
這一下子炸開了鍋, 雖然Java 帝國規定, 接口的設計一定要規範, 不能亂來, 但是大家蜂擁而至, 紛紛要求FileIO 給自己也開VIP通道。
FileIO實在是沒有辦法, 無奈之下爲小李, 小王等等都開啓了VIP通道:
村長支招
隨着FileIO開啓的VIP通道越來越多,FIleIO發現自己的體積越來越膨脹,自己有大量的代碼是在處理這些VIP通道,而且處理方式都差不多,VIP通道多了也就失去其意義了。
有一次, 張家村德高望重的村長路過FileIO這裏,FileIO知道村長軟件設計能力了得, 趕緊拉住就行討教。
村長果然見(lao)多(jian)識(ju)廣(hua),“小夥子,既然我們村的成員老是需要你的幫助,你就別爲每個人開啓一個VIP通道,你直接弄一個我們張家村的VIP通道,這個通道不是接受張大胖, 張二胖這樣的類, 而是接受一個ZhangClient的抽象類。這個抽象類中只有一個方法:onResult
每次,有人去找你幫忙的時候,你也不用管具體是誰,只要他實現了ZhangClient,你就知道它有一個onResult(false)的方法,你處理完了之後直接回調它的onResult(boolean)方法就行了,是不是很簡單啊,哈哈哈哈哈~~~”
FileIO聽完老村長的話恍然大悟,這一層解決不了的事,那我們再加一層,在上一層解決唄:
如上所示,FileIO表面上回調了ZhangClient 的onResult(false)方法,但實際上回調的是XiaoZhang的onResult(false)方法,因爲傳進來的實例實際上是繼承了ZhangClient的小張(作者:感覺像披着羊皮的狼)。
後來,帝國將這種利用抽象類去實現回調的方式稱之爲抽象類回調。
Java 巡視組
FileIO把其他通道都刪除了, 只留了一個ZhangClient通道, 現在他明白老村長的老奸巨猾了。
因爲李家村、趙家村、王家村的人都抱怨說, 我們找你保存個數據, 還得繼承一個姓Zhang的類, 實在是太扯了!
FileIO想了想, 得了, 爲了避免引起衆怒, 還是改個名稱吧, 就叫FileIOClient 。
即使是這樣, 很多人還在抱怨: 我已經繼承了一個類了, 怎麼可能再繼承你這個FileIOClient ? 不繼承就沒法保存數據, 還有沒有王法了! 還有,你這老是改來該去, 把我們都該累死了。
事情鬧大了, 上面派了個巡視組下來解決。
FileIO戰戰兢兢的給巡視組訴苦: ”我也實在是沒辦法啊, 你看Java也不允許多繼承, 我昨晚想起一個辦法, JAVA類都隱性繼承Object,能不能在Object裏面增加一個回調的方法?“
巡視組生氣的說:”別做夢了! java.lang.Object是我們的根, 那是你加方法的地方嗎?! 你整天只知道保存數據, 難道都忘了Java帝國的接口(interface)了嗎?“
FileIO被點醒了, 既然繼承的方式搞不定,那就接口好了, 接口可以隨意實現, 想實現幾個實現幾個。
在巡查組的監視下, FileIO很快修改了代碼:
不幸的是, 大家的代碼也都得改一遍, 萬幸的是, 只需要把extends FileIOClient 改爲implements IFileIOCallBack即可。
後來,帝國將這種利用接口去實現回調機制的方式稱之爲接口回調。
尾聲
張家村的小張有點落寞, 他原來獨有的回調方法現在已經被接口回調所替代,他獨有的優勢已經蕩然無存,風光不再。
更讓他煩心的事, 隨着FileIO接口的變化, 他的代碼也不斷的改來改去, 光是修改就耽誤了不少事兒,少掙了好多工分。
不就是一個回調嗎, 還繼承這個, 實現那個的, 這Java 搞的也太複雜了。
有小道消息說,Java帝國之外的動態語言王國有個叫Duck Typing 的東西, 實現回調的時候根本不用繼承什麼東西, 也不用實現什麼接口, 只要自己有一個onResult方法, 就可以被調用, 小張好奇心大起,決定去出去闖一闖。
(完)
來自:碼農翻身(微信號:coderising)
附:頭圖