Зачем использовать @Singleton над объектом Scala в Play Framework?
я использую играть! Рамки на Скала уже почти год. В настоящее время я использую версию 2.5.x.
Я знаю об эволюции контроллеров в игре и о том, как разработчики были вынуждены уйти от статики object
маршруты.
я также знаю о Guice использование в игре.
если вы скачать активатор и беги:
activator new my-test-app play-scala
Activator создаст для вас проект шаблона. Мой вопрос конкретно вокруг этой файл этого шаблона.
my-test-app / app / услуги / счетчик.скала!--34-->
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()
}
вы также можете увидеть его использование в этой:
my-test-app / app / контроллеры / CountController.скала!--34-->
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?
Это намного меньше кода.
пример:
my-test-app / app / услуги / счетчик.скала!--34-->
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 / контроллеры / CountController.скала!--34-->
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) }
}
в чем разница? Предпочтительна ли инъекция и почему?
3 ответов
является ли инъекция предпочтительным способом? Вообще да
несколько преимуществ использования инъекции зависимостей:
- отделить контроллер от конкретной реализации
Counter
.- если бы вы использовали
object
, вам нужно будет изменить контроллер, чтобы указать на другую реализацию. Например!--2-->
- если бы вы использовали
- вы можете изменить реализацию во время тестирования с помощью Guice custom фурнитура
- давайте скажем, что внутри
Counter
вы делаетеWS
звонок. Это может вызвать некоторые трудности модульного тестирования. Если вы используете инъекцию зависимостей с Guice, вы можете переопределить привязку междуCounter
иAtomicCounter
указать на автономную версиюCounter
Что вы написали специально для тестов. См.здесь для получения дополнительной информации об использовании Guice для игры тесты.
- давайте скажем, что внутри
Также см. мотивация эта пьеса была для перехода на DI.
Я говорю вообще, потому что я видел, что инъекция зависимостей идет ужасно неправильно, используя Spring и другие Java-фреймворки. Я бы сказал, что вы должны использовать свое собственное суждение, но ошибаться на стороне использования DI для игры.
может быть, потому, что одноэлементный объект Scala не может иметь параметров? Например, если у вас есть класс обслуживания, который имеет DAO, и вы хотите использовать службу в контроллере, вы должны ввести его. Самый простой способ (IMO) - DI с Guice... Кроме того, вы можете иметь свои зависимости в одном месте(модуле) и т. д...
Я не уверен, если я понимаю ваш вопрос, но инъекция предпочтительнее, потому что:
- различные части вашего приложения менее связаны
- легче заменить вашу зависимость другим классом, обеспечивающим ту же функциональность (в случае, если вам нужно будет сделать это в будущем) - вам нужно будет изменить несколько строк кода и не искать все вхождения вашего объекта
- проще протестировать (особенно, когда вам нужно издеваться что-то)
коротко говоря: D из твердых принципов: "зависите от абстракций. Не зависите от конкреций".