DIについてあれこれ

Dependency Injectionとはコンポーネント間の依存関係をプログラムのソースコードから排除し、外部の設定ファイルなどで注入できるようにするソフトウェアパターンである

ってwikipedia先生が言ってました。

Scalaにおける最適なDependency Injectionの方法を考察する 〜なぜドワンゴアカウントシステムの生産性は高いのか〜 - Qiita を読んでいろいろ考えたので、なんで今さらって感じのことを書きます。

ScalaでDIというとDIコンテナとかCake PatternとかReader Monadとかって話になっちゃうんですが、これらはいかにかっこよくDIするかの話であって、別にこういった道具やパターンを使わなくてもDIは可能という話です。

Constructor Injection

簡単な例で考えます。今ここにUserRepositoryにべったり依存しているUserServiceがあります。

class UserRepository {
  def findAll = ???
}

class UserService {
  val userRepository = new UserRepository
  def getAllUser = userRepository.findAll
}

UserServiceの中でUserRepositoryをnewしているので良くない。UserServiceのテストするのにUserRepositoryのこと考えてテストしなきゃいけなくてめんどくさい。なので

class UserRepository {
  def findAll = ???
}

class UserService(userRepository: UserRepository) {
  def getAllUser = userRepository.findAll
}

というように、UserRepositoryを外から与えるようにしよう。こうすればUserRepositoryの代わりにMockUserRepositoryを使ったりして楽にテストができますね。ちなみにこれはconstructorから注入してるからconstructor injection。

ところでUserRepositoryをnewしてUserServiceに与えるのって誰がやるんだって話になりますが、手動でやるんですね。だからこれは手動DIと呼ぶことにしましょう。

DIを手動じゃなくて自動でやりたくなったらDIコンテナを使い始めます。DIコンテナの何が便利かっていうと手動でやっていたUserRepositoryをnewしてUserServiceにつっこむというのを勝手にやってくれるということです。かっこよく言うとオブジェクトのライフサイクル管理を任せられる、といったところでしょうか。これはこれでとても便利です。

ところでScalaではCake PatternとかでDIできるからDIコンテナなんて不要、と言う煽りをよく見るんですが、おかしいですよね。JavaだろうとPHPだろうとDIコンテナがなくてもDIできるし、DIコンテナがやってくれる仕事はCake Patternではやってくれません。

PlayでのDI

PlayではDIコンテナとしてGuiceを使っていますが、DIコンテナありきで話が進んだわけではありません。constructor injectionベースで疎結合にやっていこうぜという話が出発点でした。PlayはGuiceを使ってる、っていうのは正しいけれど、気持ちはconstructor injectionなのです。なんかそういう話を前に書いてました。

tototoshi.hatenablog.com

Minimal Cake

で、Scalaにおける最適なDependency Injectionの方法を考察する 〜なぜドワンゴアカウントシステムの生産性は高いのか〜 - QiitaのMinimal Cakeの話です。

Minimal CakeはCake Patternに制約を設けたデザインパターンとして紹介されていますが、なんかピンと来ませんでした。

でも作者の方のコメントはピンと来ました。

Scala - Minimal Cake Pattern のお作法 - Qiita

しばらくの間は Minimal Cake Pattern のメリットが明らかでなかったので、コンストラクタによる注入パターンも書かれ続けていました。しかし依存先モジュールが多いクラスが生まれてくると、だんだん Minimal Cake Pattern の便利さが分かってきました。コンストラクタでは引数の順番を気にしなければいけないのに、Minimal Cake Pattern だと MixIn の継承順序はどうでもいいのです。依存先クラスが10個以上あるクラスに対して正しい順番でコンストラクタ引数を与えるのは苦行でしかありませんでした 。

Scala界隈の一連のワイワイガヤガヤは忘れましょう。ただInjectionの仕方を工夫したと考えたほうがスムーズに頭に入る気がします。

以上とりとめのない話でした