web-dev-qa-db-ja.com

Play FrameworkでScalaのオブジェクトに対して@Singletonを使用する理由

Play!Framework for Scala を1年近く使用しています。現在、バージョン 2.5.x を使用しています。

Playでのコントローラーの進化と、開発者が静的なobjectルートからどのように離脱させられたかを知っています。

Guice の使用法も知っています。

activator をダウンロードして実行した場合:

_activator new my-test-app play-scala
_

アクティベーターがテンプレートプロジェクトを作成します。私の質問は、具体的にはそのテンプレートの this ファイルについてです。

my-test-app/app/services/Counter.scala

_package services

import Java.util.concurrent.atomic.AtomicInteger
import javax.inject._

/**
 * This trait demonstrates how to create a component that is injected
 * into a controller. The trait represents a counter that returns a
 * incremented number each time it is called.
 */
trait Counter {
  def nextCount(): Int
}

/**
 * This class is a concrete implementation of the [[Counter]] trait.
 * It is configured for Guice dependency injection in the [[Module]]
 * class.
 *
 * This class has a `Singleton` annotation because we need to make
 * sure we only use one counter per application. Without this
 * annotation we would get a new instance every time a [[Counter]] is
 * injected.
 */
@Singleton
class AtomicCounter extends Counter {  
  private val atomicCounter = new AtomicInteger()
  override def nextCount(): Int = atomicCounter.getAndIncrement()
}
_

this ファイルでも使用法を確認できます。

my-test-app/app/controllers/CountController.scala

_package controllers

import javax.inject._
import play.api._
import play.api.mvc._
import services.Counter

/**
 * This controller demonstrates how to use dependency injection to
 * bind a component into a controller class. The class creates an
 * `Action` that shows an incrementing count to users. The [[Counter]]
 * object is injected by the Guice dependency injection system.
 */
@Singleton
class CountController @Inject() (counter: Counter) extends Controller {

  /**
   * Create an action that responds with the [[Counter]]'s current
   * count. The result is plain text. This `Action` is mapped to
   * `GET /count` requests by an entry in the `routes` config file.
   */
  def count = Action { Ok(counter.nextCount().toString) }

}
_

これは、@Inject() (counter: Counter)のコンストラクターを持つすべてのコントローラーがCounterの同じインスタンスを受け取ることを意味します。

だから私の質問は:

この例ではScalaオブジェクトを使用できるのに、なぜ_@Singleton_を使用してからコントローラに_@Inject_を使用するのですか?
はるかに少ないコードです。

例:

my-test-app/app/services/Counter.scala

_package services

trait ACounter {
  def nextCount: Int
}

object Counter with ACounter {
  private val atomicCounter = new AtomicInteger()
  def nextCount(): Int = atomicCounter.getAndIncrement()
}
_

次のように使用します。

my-test-app/app/controllers/CountController.scala

_package controllers

import javax.inject._
import play.api._
import play.api.mvc._

import services.{Counter, ACounter}

/**
 * This controller demonstrates how to use dependency injection to
 * bind a component into a controller class. The class creates an
 * `Action` that shows an incrementing count to users. The [[Counter]]
 * object is injected by the Guice dependency injection system.
 */
@Singleton
class CountController extends Controller {
  //depend on abstractions
  val counter: ACounter = Counter

  def count = Action { Ok(counter.nextCount().toString) }

}
_

違いはなんですか?注射が好ましいのですか、なぜですか?

28
Rhys Bradbury

注射が望ましい方法ですか?一般的にはい

依存性注入を使用することのいくつかの利点:

  1. Counter。の具体的な実装からコントローラーを切り離します
    • objectを使用する場合、別の実装を指すようにコントローラーを変更する必要があります。 EG Counter2.nextCount().toString
  2. テスト中に、Guiceカスタムバインディングを使用して実装を変更できます
    • Counterの内部でWS呼び出しを行っているとしましょう。これにより、ユニットテストが困難になる可能性があります。 Guiceで依存関係注入を使用している場合、CounterAtomicCounterの間のバインディングをオーバーライドして、テスト用に特別に作成したCounterのオフラインバージョンを指すことができます。 PlayテストでGuiceを使用する方法の詳細については、 こちら を参照してください。

PlayがDIに移行するために持っていた motivations も参照してください。

私が一般的に言っているのは、Springやその他のJava=フレームワークを使用して依存性注入がひどく間違っているのを見たからです。私はあなた自身の判断を使うべきですが、PlayにDIを使用することに関しては誤りです。

19
nattyddubbs

たぶん、Scalaのシングルトンオブジェクトはパラメーターを持つことができないためでしょうか。たとえば、DAOが挿入されているサービスクラスがあり、コントローラーでサービスを使用する場合は、それを挿入する必要があります。最も簡単な方法(IMO)は、Guiceを使用したDIです...また、依存関係を1つの場所(モジュール)などに置くことができます...

7
insan-e

あなたの質問を理解しているかどうかはわかりませんが、次の理由で注射が推奨されます。

  • アプリケーションのさまざまな部分の結合が少ない
  • 依存関係を同じ機能を提供する別のクラスに置き換える方が簡単です(将来、そうする必要がある場合)-数行のコードを変更し、オブジェクトのすべての出現箇所を探す必要はありません。
  • テストする方が簡単です(特に、モックする必要がある場合)

簡単に言えば:SOLIDの原則からのD:「抽象化に依存する。具体化に依存しない」。

4
michaJlS