這是告別CSDN后第一次使用簡書寫IT類的博客阳懂,還在適應(yīng)。最不適應(yīng)的就是不能直接手輸markdown語法標記。(好像原因是我沒有切換編輯器)
什么是響應(yīng)式編程(Reactive Programming)
In computing, reactive programming is an asynchronous programming paradigm concerned with data streams and the propagation of change. This means that it becomes possible to express static (e.g. arrays) or dynamic (e.g. event emitters) data streams with ease via the employed programming language(s), and that an inferred dependency within the associated execution model exists, which facilitates the automatic propagation of the change involved with data flow.
-- Wikipedia
以上解釋來自維基百科,在計算機領(lǐng)域,響應(yīng)式編程是一個專注于數(shù)據(jù)流和變化傳遞的異步編程范式朴读。這意味著可以使用編程語言很容易地表示靜態(tài)(例如數(shù)組)或動態(tài)(例如事件發(fā)射器)數(shù)據(jù)流,并且在關(guān)聯(lián)的執(zhí)行模型中走趋,存在著可推斷的依賴關(guān)系衅金,這個關(guān)系的存在有利于自動傳播與數(shù)據(jù)流有關(guān)的更改。
拋開大段大段的概念,我們先搞清楚一件事情:什么是編程范式氮唯?
通俗的說:編程是為了解決問題鉴吹,而解決問題可以有多種視角和思路,其中具有普適性的模式被歸結(jié)為范式惩琉。我們常說的:“面向?qū)ο蟆倍估懊嫦蜻^程”都是編程范式。
響應(yīng)式編程是一種從數(shù)據(jù)流和變化出發(fā)的解決問題的模式瞒渠。所以要研究響應(yīng)式編程良蒸,一定要牢記已經(jīng)掌握的OO(面向?qū)ο螅P者妄斷大家OO的思想都是很根深蒂固了)來做對比伍玖,也一定要拋開OO避免鉆牛角尖嫩痰。
為什么是異步?
在展開這個問題前窍箍,我們先看一個故事串纺,引自知乎:小故事
摘抄如下:
老張愛喝茶,廢話不說椰棘,煮開水造垛。
出場人物:老張,水壺兩把(普通水壺晰搀,簡稱水壺;會響的水壺办斑,簡稱響水壺)外恕。
1 老張把水壺放到火上,立等水開乡翅。(同步阻塞)
老張覺得自己有點傻2 老張把水壺放到火上鳞疲,去客廳看電視,時不時去廚房看看水開沒有蠕蚜。(同步非阻塞)
老張還是覺得自己有點傻尚洽,于是變高端了,買了把會響笛的那種水壺靶累。水開之后腺毫,能大聲發(fā)出嘀~~~~的噪音。3 老張把響水壺放到火上挣柬,立等水開潮酒。(異步阻塞)
老張覺得這樣傻等意義不大4 老張把響水壺放到火上,去客廳看電視邪蛔,水壺響之前不再去看它了急黎,響了再去拿壺。(異步非阻塞)
老張覺得自己聰明了。所謂同步異步勃教,只是對于水壺而言淤击。普通水壺,同步故源;響水壺污抬,異步。雖然都能干活心软,但響水壺可以在自己完工之后壕吹,提示老張水開了。這是普通水壺所不能及的删铃。同步只能讓調(diào)用者去輪詢自己(情況2中)耳贬,造成老張效率的低下。
所謂阻塞非阻塞猎唁,僅僅對于老張而言咒劲。立等的老張,阻塞诫隅;看電視的老張腐魂,非阻塞。情況1和情況3中老張就是阻塞的逐纬,媳婦喊他都不知道蛔屹。雖然3中響水壺是異步的,可對于立等的老張沒有太大的意義豁生。所以一般異步是配合非阻塞使用的兔毒,這樣才能發(fā)揮異步的效用。
上面這個小故事還是有點問題甸箱,但基本可以說明問題了育叁。
響應(yīng),一定是對一個事件芍殖、一個信號(諸如此類的描述)產(chǎn)生了反應(yīng)豪嗽。響水壺的響應(yīng)是什么呢?水溫達到一定程度,水壺的反應(yīng)是會響豌骏。水壺響了龟梦,聲音傳遞給老張,老張的反應(yīng)是去關(guān)水壺肯适。
再看普通水壺变秦,水溫達到一定程度,水壺沒有反應(yīng)框舔,水的反應(yīng)是冒氣泡蹦玫,冒水霧赎婚。只是這個信號不太容易傳遞,要跑過來看樱溉,所以老張只能以輪訓的方式來辦事情挣输,沒法跑到一邊等通知。
對于兩個水壺而言福贞,燒水都是阻塞的撩嚼,水沒燒完就干不了其他的事情(比如說拿來砸胡桃?挖帘?完丽?)
ok,回到我們的問題:為什么是異步?
回歸到本質(zhì)回答這個問題:響應(yīng)式編程拇舀,本質(zhì)上是對數(shù)據(jù)流或某種變化所作出的反應(yīng)逻族,但是這個變化什么時候發(fā)生是未知的,所以他是一種基于異步骄崩、回調(diào)的方式在處理問題。
怪圈:似乎絕大多數(shù)博客說著說著就開始講解RxAndroid
正如副標題要拂,在網(wǎng)上搜索到的絕大多數(shù)的博客都會說著說著就在教你如何使用RxAndroid。各位脱惰,請記住以下幾點:
RxAndroid(或RxJava)是很優(yōu)秀的響應(yīng)式編程框架。
你并非一定需要使用RxAndroid拉一。
RxAndroid并不像那些博客里面說的那樣會讓你的代碼變得更可讀彻况。
這里我直接進入第三點。取用扔物線推薦RxJava中的例子:如下這段代碼:
Observable.from(folders)
.flatMap((Func1) (folder) -> { Observable.from(file.listFiles()) })
.filter((Func1) (file) -> { file.getName().endsWith(".png") })
.map((Func1) (file) -> { getBitmapFromFile(file) })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((Action1) (bitmap) -> { imageCollectorView.addImage(bitmap) });
就這段代碼,是否需要從上到下仔細閱讀一遍之后才能才會知道他的意圖良蛮?
甚至,為了精讀代碼决瞳,他可能是這樣:
Observable.from(folders)
.flatMap(new Func1<File, Observable<File>>() {
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Bitmap>() {
@Override
public void call(Bitmap bitmap) {
imageCollectorView.addImage(bitmap);
}
});
ok,請允許我再問一個問題货徙,如此簡介的代碼,您打算單獨用一個類來放嗎皮胡?
如果對于任何一個處理類似業(yè)務(wù)邏輯的rx代碼段都使用類來放痴颊,可能類數(shù)量會爆炸屡贺,而且這些類的命名看起來會很奇葩锌杀。若不這樣泻仙,您的業(yè)務(wù)實現(xiàn)類中將充斥諸如此類不精讀不敢確定語義、容易被誤修改突想、不容易測試的代碼。面對這樣的代碼的時候只會是如履薄冰戰(zhàn)戰(zhàn)兢兢猾担。
我是在反對使用RxAndroid嗎刺下?
No,我只是反對濫用Rx怠李,我贊成對某些高度抽象的異步行為使用Rx構(gòu)建具有語義性的框架代碼,例如:編寫MVVM分層框架夷蚊。反對對任何業(yè)務(wù)細節(jié)都去做“一切皆流”的無腦工作髓介。畢竟:業(yè)務(wù)是需要逐漸迭代發(fā)展的,對于有測試代碼支撐的唐础、同時有較強語義性的類,我們泛讀代碼就可以“聞弦歌而知雅意”呀邢,對于需要重構(gòu)何處代碼豹绪,修改何處邏輯心中有數(shù)价淌,而不必將“流”再反轉(zhuǎn)回“實際的相互關(guān)系”瞒津,再打亂,修改病毡,再組織成流屁柏,再惡心下一次迭代有送,而且功戚,最關(guān)鍵的是“你可能要從很多的流中找出這一個流”。