SubCutでコンポーネントの依存性を解決する

SubCutというのはScala向けのサービスロケータ + DIコンテナのようなものらしいです。

前から気になっていたのですが、触りだけ試してみました。ドキュメントやテストケースを見る限り、以下のような感じのもののようです。

基本的にはいわゆるJavaDIコンテナのように、コンポーネントにはコンテナの存在を意識させずに透過的にバインディングを行ってくれるというものではなく、サービスロケータに近い感じです。ただしモジュール内でコンポーネントの組み立てまで行ってしまえばSubCutを意識しない作りにすることもできそうです。
というわけでやってみました。
ちょっとわざとらしい例ですが、まずはログを出力するためのトレイトとその実装を作っておきます。

trait Logger {
  def log(message: String): Unit
}

class SimpleLogger extends Logger {
  def log(message: String): Unit = {
    println(message)
  }
}

このLoggerを使用するクラスを定義します。

trait Service {
  def service(): Unit
}

class HelloService(logger: Logger) extends Service {
  def service = {
    logger.log("** start service **")
    
    // ... 本来の処理 ...
    
    logger.log("** end service **")
  }
}

HelloServiceはコンストラクタでLoggerを受け取ります。この部分の依存関係をSubCutで解決してみます。Serviceトレイトをミックスインしたクラスは複数登録するかも…ということでHelloServiceは名前付きで登録しています。

import org.scala_tools.subcut.inject._

object SampleModule extends
  MutableBindingModule  with Injectable {

  val bindingModule = this

  bind [Logger] toSingle new SimpleLogger

  bind [Service] identifiedBy("helloService") toSingle
    new HelloService(inject[Logger])
}

HelloServiceを取得して実行してみます。SampleModuleから名前を指定してHelloServiceを取得します。

val helloService = SampleModule.inject[Service](Some("helloService"))
helloService.service

モジュールもそうですが、ここでは紹介していませんがプロバイダとか、どことなくGuiceに似ている感じです。
ただ、もちろんこれだけでなく他にもいろんなことができるのですが、これならCakeパターンでもいいような…という気がしてしまいますねぇ…。