LoginSignup
31
28

More than 5 years have passed since last update.

ScalaとGoogle Guiceの組み合わせについて軽く調べた

Posted at

あまり目新しい内容ではないですが、Google Guice理解のために、改めてまとめました。

ScalaにおけるDI

Javaなどの言語と同様に、Scalaにおいても依存性注入(DI)を実現する方法はいくつかあるようです。

  1. Constructor InjectionやSetter Injectionを使う
  2. Cake PatternなどのScala特有の言語機能を使った方法
  3. (Google Guiceなどの)DIフレームワークを使う
  4. 構造的部分型を使うetc....

この記事では3.のGoogle Guiceを使う方法について調べました。そういえばPlay Frameworkの2.4でも導入されるとか聞いたような……?

Google Guiceの基本的な使い方(Java)

いくつかの典型的なパターンと比較しているという点でも、公式のチュートリアルがとても分かりやすいかなと思います。GuiceはもともとJava用なので、Javaで書いてある……。

大事な部分だけ抜き出すとこんな感じ。

interface Service {
    void doSomething();
}

class RealService implements Service {
    @Override
    public void doSomething() {
        // .....
    }
}

class FakeService implements Service {
    @Override
    public void doSomething() {
        // .....        
    }
}

やりたいことは、RealServiceFakeServiceどちらを使うかを状況によって切り替えることです。例えばテスト時にだけFakeServiceを使うようにしたい。そして、どの具体的な実装を使うかという知識は、Serviceとは離れた場所に持たせたい。Guiceでは次のようなお作法に従ってDIを実現するようです。

  • 依存性を注入されるクラスでは、依存関係にあるクラスをコンストラクタで受け取る
    • コンストラクタには@Injectアノテーションを付けておく
  • どう配線するか(どのインタフェースにどの具体的な実装を入れるか)はModuleにまとめる
  • Guice.createInjectorを用いることで、ModuleからInjectorを生成する
  • InjectorgetInstanceメソッドを呼ぶことで、Moduleで記述された参照関係に基づいて実際のインスタンスが得られる

このルールに基づいて、Serviceを利用するクラスであるServiceUserを記述してみます。

class ServiceUser {
    private final Service service;

    @Inject
    public ServiceUser(Service service) {
        this.service = service;
    }
    // serviceを利用したコードは省略...
}

具体的にどのServiceを利用するかは、ServiceUserに現れていません。次はようやく、ServiceUserを使う例です。

public class ServiceModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(Service.class).to(RealService.class);
  }
}
public static void main(String[] args) {
    Injector injector = Guice.createInjector(new ServiceModule());
    ServiceUser serviceUser = injector.getInstance(ServiceUser.class);
    // serviceUserを使ったコードは省略.....
}

Moduleを切り替えることで、依存する実装も切り替えることができるようになりました。

ScalaでGuiceはどうなるのか

依存関係にGoogle Guice 4.0を追加します。

libraryDependencies += "com.google.inject" % "guice" % "4.0"

Scalaでの使い方としては、Javaとほとんど同じように書けます。@Injectの位置が、クラス名の後ろ、コンストラクタ引数の前になるという点に注意。

object TestDI {
  import javax.inject._
  import com.google.inject.AbstractModule

  trait Service {
    def doSomething(): Unit
  }

  class RealService extends Service {
    def doSomething() { println("This is RealService") }
  }

  class FakeService extends Service {
    def doSomething() { println("This is FakeService") }
  }

  class ServiceUser @Inject() (service: Service) {
    def use() { service.doSomething() }
  }

  class ServiceModule extends AbstractModule {
    override protected def configure() {
      bind(classOf[Service]).to(classOf[RealService])
    }
  }
}

使う時もJavaとほぼ同様です。

import com.google.inject._
val injector: Injector = Guice.createInjector(new ServiceModule)
val user: ServiceUser = injector.getInstance(classOf[ServiceUser])
user.use //=> "This is RealService"

まとめ

Scalaでも、おおむねJavaと同じようにGuiceは使えることが分かりました……が、JavaのものをそのままScalaに移しても、あんまりScalaっぽくならないので微妙感が漂う気がします。なんかもうちょっとラップしたライブラリが欲しくなるなる……。

Playなどのフレームワークと統合した場合にGuiceはどういった使い勝手になるのかについては、今後調査したいですね……。

参考にしたもの

31
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
28