我已经将Play! Framework用于Scala近一年了。我目前正在使用版本2.5.x。
我知道Play中控制器的发展以及开发人员如何被迫远离静态object
路由。
我也知道游戏中的Guice用法。
如果您下载activator并运行:
activator new my-test-app play-scala
Activator将为您生成一个模板项目。 我的问题是关于该模板的this文件。
我测试应用程式/应用/服务/ 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文件中查看其用法:
我测试应用程式/应用/控制器/ 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
的相同实例。
所以我的问题是:
为什么将@Singleton
然后@Inject
用于控制器,本示例中您可以使用Scala对象?
它的代码少得多。
示例:
我测试应用程式/应用/服务/ Counter.scala
package services
trait ACounter {
def nextCount: Int
}
object Counter with ACounter {
private val atomicCounter = new AtomicInteger()
def nextCount(): Int = atomicCounter.getAndIncrement()
}
像这样使用它:
我测试应用程式/应用/控制器/ 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) }
}
有什么区别?注射是首选,为什么?
答案 0 :(得分:18)
注射是首选方式吗?一般是的
使用依赖注入的一些优点:
Counter
的具体实现分离。
object
,则必须更改控制器以指向不同的实现。 EG Counter2.nextCount().toString
Counter
内部正在进行WS
通话。这可能会导致单元测试的一些困难。如果您正在使用Guice的依赖注入,则可以覆盖Counter
和AtomicCounter
之间的绑定,以指向您专门为测试编写的Counter
的脱机版本。有关使用Guice for Play测试的详细信息,请参阅here。另请参阅Play用于迁移到DI的motivations。
我之所以这么说,是因为我已经看到使用Spring和其他Java框架的依赖注入非常错误。我说你应该使用自己的判断,但在使用DI进行游戏时会犯错。
答案 1 :(得分:6)
也许是因为Scala的单身对象无法获得参数?例如,如果您有一个注入了DAO的服务类,并且您想在控制器中使用服务,则必须注入它。最简单的方法(IMO)是DI with Guice ...此外,您可以将您的依赖项放在一个地方(模块)等...
答案 2 :(得分:4)
我不确定,如果我理解你的问题,但注射是首选,因为:
简短地说:来自SOLID原则的D:“取决于抽象。不要依赖于结核”。