【譯】避免打斷鏈?zhǔn)浇Y(jié)構(gòu):使用.compose( )操作符

*[Chains break by the weakest link](https://www.flickr.com/photos/hernanpc/7115374283)*

RxJava的另一個(gè)好處在于,我們可以清楚地看到數(shù)據(jù)是如何在一系列操作符之間進(jìn)行轉(zhuǎn)換的。

Observable.from(someSource)
    .map(new Func1<Data, Data>() {
      @Override public Data call(Data data) {
        return manipulate(data);
      }
    }).subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<Data>() {
      @Override public void call(Data data) {
        doSomething(data);
      }
    });

如何將一組操作符重用于多個(gè)數(shù)據(jù)流中呢?例如,因?yàn)橄M诠ぷ骶€程中處理數(shù)據(jù),然后在主線程中處理結(jié)果,所以我會(huì)頻繁使用subscribeOn()observeOn()。如果我能夠通過(guò)重用的方式,將這種邏輯運(yùn)用到我所有的數(shù)據(jù)流中,將是一件多么偉大的事。

糟糕的實(shí)現(xiàn)方式

下面這些代碼是我在過(guò)去幾個(gè)月里一直都在使用的,正好可以拿來(lái)當(dāng)反面教材。

首先,使用schedulers(調(diào)度)創(chuàng)建一個(gè)方法:

<T> Observable<T> applySchedulers(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread());
}

然后,封裝Observable數(shù)據(jù)鏈:

applySchedulers(Observable.from(someSource).map(new Func1<Data, Data>() {
      @Override public Data call(Data data) {
        return manipulate(data);
      }
    })
).subscribe(new Action1<Data>() {
  @Override public void call(Data data) {
    doSomething(data);
  }
});

雖然這樣做也能達(dá)到目的,但是它看起來(lái)不僅丑,還容易讓人產(chǎn)生困惑,applySchedulers()到底什么鬼?它不再符合操作符鏈路式結(jié)構(gòu),所以,看起來(lái)很難理解。然而,我找不到任何辦法去格式化這段代碼,因此,這并不尷尬。

現(xiàn)在,試想一下,如果在一個(gè)數(shù)據(jù)流中反復(fù)使用的話,這個(gè)反面教材將會(huì)變得要多爛有多爛。(譯者注:OMG)

Transformers簡(jiǎn)介

聰明的同學(xué)可能已經(jīng)意識(shí)到了這個(gè)問(wèn)題,但是RxJava早已提供了一種解決方案:Transformer(譯者注:有轉(zhuǎn)換器意思),一般情況下可以通過(guò)使用操作符Observable.compose()來(lái)實(shí)現(xiàn)。

Transformer實(shí)際上就是一個(gè)Func1<Observable<T>, Observable<R>>,換言之就是:可以通過(guò)它將一種類(lèi)型的Observable轉(zhuǎn)換成另一種類(lèi)型的Observable,和調(diào)用一系列的內(nèi)聯(lián)操作符是一模一樣的。

接下來(lái),我們一起創(chuàng)建一個(gè)Transformerschedulers相結(jié)合的方法:

 <T> Transformer<T, T> applySchedulers() {
 return new Transformer<T, T>() {
   @Override
   public Observable<T> call(Observable<T> observable) {
     return observable.subscribeOn(Schedulers.io())
         .observeOn(AndroidSchedulers.mainThread());
   }
 };
}

使用Lambda表達(dá)式,會(huì)變得更加優(yōu)雅:

<T> Transformer<T, T> applySchedulers() {  
return observable -> observable.subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread());
}

更新于2015年3月11日:如果使用 JDK 7或者更早版本進(jìn)行編譯的話,就不得不做出一些額外的改變,為compose()添加泛型。顯式聲明返回類(lèi)型,就像這樣:

 Observable.from(someSource)
    .map(new Func1<Data, Data>() {
       @Override public Data call(Data data) {
         return manipulate(data);
       }
    })
    .compose(this.<YourType>applySchedulers())
    .subscribe(new Action1<Data>() {
      @Override public void call(Data data) {
        doSomething(data);
      }
    });

重用Transformers

上一個(gè)實(shí)例中,我在所有的方法調(diào)用中都new(譯者注:新建)了一個(gè)Transformer實(shí)例。你可以創(chuàng)建一個(gè)實(shí)例化版本,節(jié)省不必要的實(shí)例化對(duì)象。畢竟,Transformers關(guān)乎著代碼重用。

如果,總是將一個(gè)具體的類(lèi)型轉(zhuǎn)換成另一個(gè)具體的類(lèi)型,那么可以很容易的創(chuàng)建一個(gè)Transformer實(shí)例:

Transformer<String, String> myTransformer = new Transformer<String, String>() {  
   // ...Do your work here...
};

那么scheduler Transformer(調(diào)度轉(zhuǎn)換器)應(yīng)該怎樣實(shí)現(xiàn)呢?因?yàn)?,它根本不需要關(guān)心類(lèi)型,所以就無(wú)法定義一個(gè)泛型實(shí)例:

// Doesn't compile; where would T come from?(譯者注:編譯無(wú)法通過(guò);因?yàn)楦静恢?T 從何而來(lái)。)
Transformer<T, T> myTransformer;  

可能有人會(huì)這樣定義Transformer<Object, Object>,然而這并沒(méi)有什么卵用,并且造成的后果就是Observable將失去其類(lèi)型信息。

為了解決這個(gè)問(wèn)題,我從Collections中得到了一些啟發(fā),這個(gè)包裝類(lèi)有這樣一堆方法,能夠創(chuàng)建類(lèi)型安全并且不可變的空集合(比如,Collections.emptyList())。由于在其內(nèi)部使用了一個(gè)無(wú)泛型實(shí)例,所以需要封裝在一個(gè)添加泛型約束的方法里。

可以像下面這樣定義我們的schedulers Transformer(調(diào)度轉(zhuǎn)換器)實(shí)例:

 final Observable.Transformer schedulersTransformer = new  Observable.Transformer() {
 @Override public Object call(Object observable) {
   return ((Observable)  observable).subscribeOn(Schedulers.newThread())
       .observeOn(AndroidSchedulers.mainThread());
  }
};

 <T> Observable.Transformer<T, T> applySchedulers() {
 return (Observable.Transformer<T, T>) schedulersTransformer;
}

現(xiàn)在我們終于把他做成一個(gè)“單例”了(譯者注:此單例非彼單例)。

警告:如果不做類(lèi)型轉(zhuǎn)換檢查,可能陷入麻煩。確保Transformer真正與類(lèi)型無(wú)關(guān)。否則,即使代碼通過(guò)了編譯,在運(yùn)行時(shí)仍然存在拋出ClassCastException異常的隱患。在當(dāng)前場(chǎng)景中,我們知道它是安全的,因?yàn)?code>schedulers(譯者注:調(diào)度)并不會(huì)與發(fā)送出的事件產(chǎn)生任何的互動(dòng)操作。

** flatMap()操作符怎么樣?**

現(xiàn)在你可能會(huì)好奇,compose()操作符和flatMap()操作符有何區(qū)別。他們最終都會(huì)發(fā)送出Observable<R>,這就意味著,兩者都能夠用于操作符的重用?

不同點(diǎn)在于compose()操作符擁有更高層次的抽象概念:它操作于整個(gè)數(shù)據(jù)流中,不僅僅是某一個(gè)被發(fā)送的事件。具體如下:

  1. compose()是唯一一個(gè)能夠從數(shù)據(jù)流中得到原始Observable<T>的操作符,所以,那些需要對(duì)整個(gè)數(shù)據(jù)流產(chǎn)生作用的操作(比如,subscribeOn()observeOn())需要使用compose()來(lái)實(shí)現(xiàn)。相較而言,如果在flatMap()中使用subscribeOn()或者observeOn(),那么它僅僅對(duì)在flatMap()中創(chuàng)建的Observable起作用,而不會(huì)對(duì)剩下的流產(chǎn)生影響(譯者注:深坑,會(huì)在后面的系列著重講解,歡迎關(guān)注)。

  2. 當(dāng)創(chuàng)建Observable流的時(shí)候,compose()會(huì)立即執(zhí)行,猶如已經(jīng)提前寫(xiě)好了一個(gè)操作符一樣,而flatMap()則是在onNext()被調(diào)用后執(zhí)行,onNext()的每一次調(diào)用都會(huì)觸發(fā)flatMap(),也就是說(shuō),flatMap()轉(zhuǎn)換每一個(gè)事件,而compose()轉(zhuǎn)換的是整個(gè)數(shù)據(jù)流。

  3. 因?yàn)槊恳淮握{(diào)用onNext()后,都不得不新建一個(gè)Observable,所以flatMap()的效率較低。事實(shí)上,compose()操作符只在主干數(shù)據(jù)流上執(zhí)行操作。

如果想重用一些操作符,還是使用compose()吧,雖然flatMap()的用處很多,但作為重用代碼這一點(diǎn)來(lái)講,并不適用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,698評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,202評(píng)論 3 426
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,742評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,580評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,297評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,688評(píng)論 1 327
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,875評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,438評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,183評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,384評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,612評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,022評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,297評(píng)論 1 292
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,093評(píng)論 3 397
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,330評(píng)論 2 377

推薦閱讀更多精彩內(nèi)容