Celeste Engineer

Androidとか自転車とか

ライブラリやフレームワークがもたらすいくつかの問題への考え方の指針

@konifar さんの Guava にまつわる以下の記事をみて、ライブラリやフレームワークへのロックイン問題を含めいろいろな問題は、気にしなくていい場合と気にした方がいい場合があるなと思ったので、その考え方の指針みたいなものをまとめようと思います。

konifar-zatsu.hatenadiary.jp

1. ライブラリ・フレームワークの大きさがもたらす問題

Android (というか広く一般に JVM) では、宣言できるメソッド数に制限があります。このため、依存するライブラリやフレームワークで宣言されたメソッド数も考慮に入れないと、アプリケーションがビルドできなくなったり、うまく動かなくなったりします。

Android においては、これに対応するものとして、Multi-Dex という仕組みが用意されています。通常、ひとつの apk にはひとつの dex ファイルを生成しますが、Multi-Dex によって、ひとつの apk に複数の dex ファイルを生成することができるようになります。これで、メソッド数の制限を回避しています。Lollipop 以降であれば OS がサポートしてくれますが、KitKat 以前ではライブラリに依るサポートが必要となります。

qiita.com

qiita.com

画期的な仕組みではありますが、一方でデメリットもあり、アプリケーションのプロセスが起動した時に、読み込むべきファイルが複数になるため、必然的に起動が重くなります。ですので、できれば使用は避けておきたいですね。 そうなると、大きなライブラリやフレームワークの導入は慎重に行うことになります。

ただ、ライブラリやフレームワークの大きさというのはただ単に、メソッド数の制限に引っかかりやすくなる、というの問題のみをはらんでいるわけではありません。

2. ライブラリ・フレームワークの解決する問題の大きさがもたらす問題

平たく言えば、抽象度が高いほど解決する問題も広く大きくなります。あるいは、先のブログ記事でも触れられている Guava のような、ユーティリティを集めたライブラリも、解決する問題がユーティリティの数だけ増えるので、大きくなりがちです。

抽象度の高さによる解決する問題の広さが招く問題は場合によりけりだと思いますが、ユーティリティを集めたようなライブラリだと、知らないと使わないものが出てきて無駄が増えがちになる、ということは言えそうです(@konifar さんのブログでもそのような場面が書かれています)。

ProGuard によって無駄になっているものを消すのもひとつの手立てではありますが、ProGuard はビルド時間に少なからず影響を出してしまうため、出来ることなら、使う分だけ入れるぐらいで留めておきたくはあります。その意味では、ユーティリティは自分たちで使いたいものだけ手元においておくようなつまみ食いをするのも良いかなと思います。

3. ライブラリ・フレームワークへのロックインがもたらす問題

抽象度の高さで問題になることで大きく取り上げられるうちのひとつに"ロックイン"があります。抽象度が高ければそれだけ応用が効くので、アプリケーションの実装のあちこちで使えるというのがフレームワークの役目ではありますが、あまりにあちこちで使ってしまうと、それ以外のフレームワークへ変えようとした時に様々な問題が表出して困る、あるいは、他のフレームワークを常用している人にとっての学習コストがついてまわる、というのがざっくりとしたロックインの問題点です。

どのようなライブラリ・フレームワークを使うかにも依存しますが、ロックインする部分をまるごと切り離せる作りになっていて、それを使いたい場所で適宜 DI コンテナなどの仕組みを用いて差し込むことができれば、気軽にライブラリやフレームワークを変更することが出来そうです。もちろん、切り離された部分の内部では、選んだライブラリ・フレームワークに合わせた実装をする必要がありますが、アプリケーション全体としてみると、一部を変えるだけで済む作りになります。

おそらく、肌感覚としては継承を前提としていたり、特定の設計パターンに合わせた作りをできるようにしてくれるライブラリ・フレームワークはこの切り離す設計が難しいように感じます。例えば、設計を支援する系のライブラリ・フレームワーク(所謂 MVP や MVVM などのパターンを簡単に実装できるようにしてくれるものたち)は、パターンに合わせた実装が必要になるため、他のパターンに乗り換えるときには、ごっそり書き換えないといけなくなります。アプリケーション全体で設計パターンをごっそり変える必要があるかどうかというのはまた別の問題として議論の余地があるかと思いますが、そうなった時に、ごっそり書き換えるコストを甘受するかどうか、という点は考えておくとよいかもしれませんね。

学習コストについては、ライブラリやフレームワークの実装を学習するよりも、もっと概念的な部分で理解をしておくほうが良い気がします。先ほどの MVP や MVVM などのパターンであれば、それぞれライブラリでどう実現しているかという部分も勿論重要ではありますが、それ以上に MVP や MVVM がどんな概念なのかという方が大事で、そこを抑えられれば、あとは実装の方法論の違いでしかなくなります。Rx の場合は方法論もそれはそれで色々ありすぎて困る部分はありますが。

まとめ

あまり"コレだッ"と言える素敵な解決法は見えてきませんでしたが、ライブラリやフレームワークを使うことで得られるメリットと、その裏にあるコストの部分を天秤にかけた時に、コストを最小化したり、デメリットを局所化するだけの用意があれば、大きいライブラリ・フレームワークもガシガシ使っていけそうだな、と言う感じはあります。たぶんここで書いたこと以外にもそういう対処法や指針は有る気がするので、こういう時はどうする・どうした・どうしたい、みたいなのが集まってくるとよさそうです。