Observableでリストを得るインターフェイスの設計の具体例を少々

RxJava Advent Calendar 2015の最終日です。軽めにいきます。

Observableでリストを得るインターフェイスObservable<T> にすべきか Single<List<T>> にするべきか迷うことがあると思います。

結論からいえば、迷った時は Single<List<T>> だろうと思っています。

List<T> を得るときは、ほとんどの場合 ListAdapter などにそのまま渡すことになるでしょう。したがって、 Observable<T> インターフェイスがあっても結局 Observable#toList()Observable<List<T>> に変換することになります。

また、典型的な二つのケース、Web API Clientを使う場合とlocal DBを使う場合を考えます。

Web API Clientの場合

Web API clientを使う場合は、T をそのまま返すのではなくresponse objectを返すべきです。実際に、Retrofit 2.0ではそのようなことができるインターフェイスになっています。

retrofit/RxJavaCallAdapterFactoryTest.java at master · square/retrofit · GitHub

  // RxJavaCallAdapterFactoryTest.java

  interface Service {
    @GET("/") Observable<String> observableBody(); # Tそのまま
    @GET("/") Observable<Response<String>> observableResponse(); # retrofit2のResponse<T>
    @GET("/") Observable<Result<String>> observableResult(); # adapter-rxjavaのResult<T> (Response<T>のラッパー)
    @GET("/") Single<String> singleBody();
    @GET("/") Single<Response<String>> singleResponse();
    @GET("/") Single<Result<String>> singleResult();
  }

したがって Single<List<T>>Observable<List<T>> かという議論は必要ありませんが、実際には Single<Result<List<T>>> だったりもするので実質的には Single<List<T>> と同じことです。

DBから取得した値の場合

DB(SQLite)からの読みこみは十分に速いので、まとめて読みこんでListAdapter に渡すのではなく、ひとつのセルを構築するたびにDB から値を一つ読み込むという方法でも実用上問題ありません。そしてこの場合は、RxJavaインターフェイスを使う必要はありません。

RxJavaのオペレータを使いたい場合は Single<List<T>> を得られれば flatMapObservable()Observable<T> に変換することもできますから、 Single<List<T>> だけあれば十分かと思います。

List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");

// Single<List<String>> があるとする
Single<List<String>> listSingle = Single.just(list);

// flatMapObservable() で Observable<String> に変換する
listSingle.flatMapObservable(new Func1<List<String>, Observable<String>>() {
    @Override
    public Observable<String> call(List<String> strings) {
        return Observable.from(strings);
    }
}).forEach(new Action1<String>() {
    @Override
    public void call(String s) {
        Log.d("XXX", s); // "foo", "bar"
    }
});

以上です。