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

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

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

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);
      }
    });

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

糟糕的實現方式

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

首先,使用schedulers(調度)創建一個方法:

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

然后,封裝Observable數據鏈:

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);
  }
});

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

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

Transformers簡介

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

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

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

 <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表達式,會變得更加優雅:

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

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

 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

上一個實例中,我在所有的方法調用中都new(譯者注:新建)了一個Transformer實例。你可以創建一個實例化版本,節省不必要的實例化對象。畢竟,Transformers關乎著代碼重用。

如果,總是將一個具體的類型轉換成另一個具體的類型,那么可以很容易的創建一個Transformer實例:

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

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

// Doesn't compile; where would T come from?(譯者注:編譯無法通過;因為根本不知道 T 從何而來。)
Transformer<T, T> myTransformer;  

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

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

可以像下面這樣定義我們的schedulers Transformer(調度轉換器)實例:

 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;
}

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

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

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

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

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

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

  2. 當創建Observable流的時候,compose()會立即執行,猶如已經提前寫好了一個操作符一樣,而flatMap()則是在onNext()被調用后執行,onNext()的每一次調用都會觸發flatMap(),也就是說,flatMap()轉換每一個事件,而compose()轉換的是整個數據流。

  3. 因為每一次調用onNext()后,都不得不新建一個Observable,所以flatMap()的效率較低。事實上,compose()操作符只在主干數據流上執行操作。

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容