Зачем использовать @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 ответов


является ли инъекция предпочтительным способом? Вообще да

несколько преимуществ использования инъекции зависимостей:

  1. отделить контроллер от конкретной реализации Counter.
    • если бы вы использовали object, вам нужно будет изменить контроллер, чтобы указать на другую реализацию. Например!--2-->
  2. вы можете изменить реализацию во время тестирования с помощью Guice custom фурнитура
    • давайте скажем, что внутри Counter вы делаете WS звонок. Это может вызвать некоторые трудности модульного тестирования. Если вы используете инъекцию зависимостей с Guice, вы можете переопределить привязку между Counter и AtomicCounter указать на автономную версию Counter Что вы написали специально для тестов. См.здесь для получения дополнительной информации об использовании Guice для игры тесты.

Также см. мотивация эта пьеса была для перехода на DI.

Я говорю вообще, потому что я видел, что инъекция зависимостей идет ужасно неправильно, используя Spring и другие Java-фреймворки. Я бы сказал, что вы должны использовать свое собственное суждение, но ошибаться на стороне использования DI для игры.


может быть, потому, что одноэлементный объект Scala не может иметь параметров? Например, если у вас есть класс обслуживания, который имеет DAO, и вы хотите использовать службу в контроллере, вы должны ввести его. Самый простой способ (IMO) - DI с Guice... Кроме того, вы можете иметь свои зависимости в одном месте(модуле) и т. д...


Я не уверен, если я понимаю ваш вопрос, но инъекция предпочтительнее, потому что:

  • различные части вашего приложения менее связаны
  • легче заменить вашу зависимость другим классом, обеспечивающим ту же функциональность (в случае, если вам нужно будет сделать это в будущем) - вам нужно будет изменить несколько строк кода и не искать все вхождения вашего объекта
  • проще протестировать (особенно, когда вам нужно издеваться что-то)

коротко говоря: D из твердых принципов: "зависите от абстракций. Не зависите от конкреций".