woshidan's blog

あいとゆうきとITと、とっておきの話。

DIパターンとストラテジーパターンの違い

potatotipsで紹介されたKotlinのアプリのリポジトリを読んでいたら使ったことの無いlateinitというAPIが出てきて、@Injectというアノテーションがあるから、おそらくこれはDIだな、と思ったのですが、よくわからず。

いつの間にかKotlinでなくDIの勉強をしていました。

DI(Dependency Injection)とは何なのか

まず最初に、下記の記事と、下記の参考に載っていた記事を読みました。

http://kazy.hatenablog.com/entry/2014/01/11/194142

http://www.atmarkit.co.jp/ait/articles/0504/29/news022.html

http://qiita.com/hshimo/items/1136087e1c6e5c5b0d9f

自分には上記の記事での依存という辺りの言葉遣いが分かり難かったので、もう少し噛み砕いてみます。

  • DIは他のクラスのインスタンスを処理に利用する側のクラスと利用される側のクラスの話
    • もう少し背伸びしてJavaらしく言うと、あるクラスをコンポジションして処理を転送する側のクラスと、処理が転送されてくる側のクラスについての話*1
  • 便宜上、ここでは、利用する側のクラスをクライアント、利用される側で処理に必要な具体的な実行メソッドを持つクラスを具象クラスと呼ぶことにする
  • 具象クラスのインスタンスをクライアントのコンストラクタなどで生成しているような状態はよくないですよね
    • 具象クラスのインスタンスの生成タイミング、生成されるインスタンスに渡される/持っているパラメータ等が固定となっている状態ともいう
      • さらに状況を絞って具体的に言うと、その具象クラスの処理の部分をモックに置き換えたくても置き換えられない状態
    • こういう状態では、クライアントを利用するとき、常に具象クラスのコードを強く意識する必要があって、テストしにくいし、使い回しづらい
    • DI用語で「利用者となるクラスは利用される具象クラスに依存している」
  • 具象クラスのインスタンスを実行時に*2代入することを「依存性を注入する」といっているらしい
    • 例えば、テストの時などは、テストの実行時に具象クラスをモッククラス等に設定できるようにする、とか

そして、具体的な実装方法としては、クライアントが具象クラスの代わりに、インタフェースをプロパティに持つようにする、ということです。

もしかしたら、意味が分かり難くなるのは、日本語の略がやたら抽象的なせいかもしれず、依存性 => 具象クラスが継承しているインタフェース、あるいはインタフェースに括り出せる性質、くらいに捉えて良さそうです。

...なるほど。

Strategyパターンと何が違うのか

ところで、「Contextクラス(利用者側のクラス)がStrategyのインタフェース(抽象クラス)をプロパティに持っていて、必要に応じてStrategyの具象クラスを切り替えることが出来る」というような設計パターンをStrategyパターンとよびます。

DIパターンは、Strategyパターンと何が違うのでしょうか。

もう少し調べてみたら、下記の記事に当たりました。

http://www.infoq.com/jp/news/2007/12/does-di-pay-off

上記の記事から引用いたしますと、

あなたがGoFのファンであるなら、これは実際Strategyパターンなのです。 依存性注入は(私の見るところ)基本的にひとまとめに使われるStrategyパターンです。

はい、GoFファンです。ところで、ひとまとめに、というところが気になります。

もう少し、先ほどの記事を読んでみます。

DIフレームワークに依存するコードをひとたび書き始めると、オブジェクト接続に要する費用はほとんどゼロになります。

 

現実世界のシナリオでは、手動で行う依存性注入はただ単にスケールしないのです。

ふむふむ、さらに、

やはりあなた方のDependency Injectionはまちがっている。 — A Day in Serenity (Reloaded) — PHP, FuelPHP, Linux or something

などを読みますと、

  • Strategyパターンも、DIパターンもインタフェースをプロパティに持って、具象クラスを切り替えられるようにしている
  • DIパターンを実装したフレームワークは、それを手動で行うのではなく、自動で行う方法を用意することで、具象クラスの切り替えを行いやすくしている
    • 切り替える内容等を書いていく設定ファイルをDIコンテナという

ということみたいです。

実際、DI用ライブラリ(DaggerやGuice)は、具象クラスの代入処理と具象クラスの代入先/代入内容を設定するAPIを持っていますね。

小規模なアプリやテストの書きはじめですとそんなこともないのですが、大規模アプリだとあった方がテストを書くのが捗りそうです。

たとえば、自分のプロジェクトでは、RetrofitのMockClientのインスタンスとClientのパラメータを手動で書き換えててだるかったのですが、そういう場合に使えないか、検討してみると良さそうですね。

*1:http://qiita.com/mikamikuh@github/items/1cdcd8b25a2e23f10525

*2:あるいは、クライアントを呼び出しているコードブロックにおいて