避免打斷鏈式結構:使用.compose( )操作符

發表於2015-12-21

RxJava的另一個好處在於,我們可以清楚地看到資料是如何在一系列操作符之間進行轉換的。

如何將一組操作符重用於多個資料流中呢?例如,因為希望在工作執行緒中處理資料,然後在主執行緒中處理結果,所以我會頻繁使用subscribeOn()observeOn()。如果我能夠通過重用的方式,將這種邏輯運用到我所有的資料流中,將是一件多麼偉大的事。

糟糕的實現方式

下面這些程式碼是我在過去幾個月裡一直都在使用的,正好可以拿來當反面教材。

首先,使用schedulers(排程)建立一個方法:

然後,封裝Observable資料鏈:

雖然這樣做也能達到目的,但是它看起來不僅醜,還容易讓人產生困惑,applySchedulers()到底什麼鬼?它不再符合操作符鏈路式結構,所以,看起來很難理解。然而,我找不到任何辦法去格式化這段程式碼,因此,這並不尷尬。

現在,試想一下,如果在一個資料流中反覆使用的話,這個反面教材將會變得要多爛有多爛。(譯者注:OMG)

Transformers簡介

聰明的同學可能已經意識到了這個問題,但是RxJava早已提供了一種解決方案:Transformer(譯者注:有轉換器意思),一般情況下可以通過使用操作符Observable.compose()來實現。

Transformer實際上就是一個Func1, Observable>,換言之就是:可以通過它將一種型別的Observable轉換成另一種型別的Observable,和呼叫一系列的內聯操作符是一模一樣的。

接下來,我們一起建立一個Transformerschedulers相結合的方法:

使用Lambda表示式,會變得更加優雅:

更新於2015年3月11日:如果使用 JDK 7或者更早版本進行編譯的話,就不得不做出一些額外的改變,為compose()新增泛型。顯式宣告返回型別,就像這樣:

 

重用Transformers

上一個例項中,我在所有的方法呼叫中都new(譯者注:新建)了一個Transformer例項。你可以建立一個例項化版本,節省不必要的例項化物件。畢竟,Transformers關乎著程式碼重用。

如果,總是將一個具體的型別轉換成另一個具體的型別,那麼可以很容易的建立一個Transformer例項:

那麼scheduler Transformer(排程轉換器)應該怎樣實現呢?因為,它根本不需要關心型別,所以就無法定義一個泛型例項:

可能有人會這樣定義Transformer,然而這並沒有什麼卵用,並且造成的後果就是Observable將失去其型別資訊。

為了解決這個問題,我從Collections中得到了一些啟發,這個包裝類有這樣一堆方法,能夠建立型別安全並且不可變的空集合(比如,Collections.emptyList())。由於在其內部使用了一個無泛型例項,所以需要封裝在一個新增泛型約束的方法裡。

可以像下面這樣定義我們的schedulers Transformer(排程轉換器)例項:

現在我們終於把他做成一個“單例”了(譯者注:此單例非彼單例)。

警告:如果不做型別轉換檢查,可能陷入麻煩。確保Transformer真正與型別無關。否則,即使程式碼通過了編譯,在執行時仍然存在丟擲ClassCastException異常的隱患。在當前場景中,我們知道它是安全的,因為schedulers(譯者注:排程)並不會與傳送出的事件產生任何的互動操作。

flatMap()操作符怎麼樣?

現在你可能會好奇,compose()操作符和flatMap()操作符有何區別。他們最終都會傳送出Observable,這就意味著,兩者都能夠用於操作符的重用?

不同點在於compose()操作符擁有更高層次的抽象概念:它操作於整個資料流中,不僅僅是某一個被髮送的事件。具體如下:

  1. compose()是唯一一個能夠從資料流中得到原始Observable的操作符,所以,那些需要對整個資料流產生作用的操作(比如,subscribeOn()observeOn())需要使用compose()來實現。相較而言,如果在flatMap()中使用subscribeOn()或者observeOn()那麼它僅僅對在flatMap()中建立的Observable起作用,而不會對剩下的流產生影響(譯者注:深坑,會在後面的系列著重講解,歡迎關注)。
  2. 當建立Observable流的時候,compose()會立即執行,猶如已經提前寫好了一個操作符一樣,而flatMap()則是在onNext()被呼叫後執行,onNext()的每一次呼叫都會觸發flatMap(),也就是說,flatMap()轉換每一個事件,而compose()轉換的是整個資料流。
  3. 因為每一次呼叫onNext()後,都不得不新建一個Observable,所以flatMap()的效率較低。事實上,compose()操作符只在主幹資料流上執行操作。

如果想重用一些操作符,還是使用compose()吧,雖然flatMap()的用處很多,但作為重用程式碼這一點來講,並不適用。

相關文章