patron explicacion ejercicios ejemplos ejemplo diseño con scala object playframework guice

scala - explicacion - singleton javascript



¿Por qué usar @Singleton sobre el objeto de Scala en Play Framework? (3)

¡He estado usando Play! Framework for Scala desde hace casi un año. Actualmente estoy usando la versión 2.5.x.

Soy consciente de la evolución de los controladores en Play y de cómo los desarrolladores se han visto obligados a abandonar las rutas de object estáticos.

También estoy enterado del uso de Guice en el juego.

Si descargas el activador y ejecutas:

activator new my-test-app play-scala

Activator producirá un proyecto de plantilla para usted. Mi pregunta es específicamente sobre este archivo de esa plantilla.

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() }

También puedes ver su uso en este archivo:

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) } }

Esto significa que cada controlador que tenga el constructor de @Inject() (counter: Counter) recibirá la misma instancia de Counter .

Entonces mi pregunta es:

¿Por qué usar @Singleton y luego @Inject it en un controlador, cuando para este ejemplo podría simplemente usar un objeto Scala?
Es mucho menos código.

Ejemplo:

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() }

Úselo así:

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) } }

¿Cuál es la diferencia? ¿Se prefiere la inyección y por qué?


¿La inyección es la forma preferida? Generalmente sí

Un par de ventajas de usar inyección de dependencia:

  1. Desacople el controlador de la implementación concreta de Counter .
    • Si tuviera que usar un object , tendría que cambiar su controlador para apuntar a la implementación diferente. EG Counter2.nextCount().toString
  2. Puede variar la implementación durante la prueba utilizando enlaces personalizados de Guice
    • Digamos que dentro de Counter estás haciendo una llamada WS . Esto podría causar algunas pruebas de unidad de dificultad. Si está utilizando la inyección de dependencia con Guice, puede anular la vinculación entre Counter y AtomicCounter para apuntar a una versión offline de Counter que haya escrito específicamente para sus pruebas. Consulte aquí para obtener más información sobre cómo usar las pruebas de Guice for Play.

También vea las motivaciones que Play tuvo para migrar a DI.

Digo en general porque he visto que la inyección de dependencias es terriblemente incorrecta usando Spring y otros frameworks de Java. Yo diría que debes usar tu propio juicio, pero te equivocarás al usar DI para jugar.


¿Tal vez porque el objeto singleton de Scala no puede tener parámetros? Por ejemplo, si tiene una clase de servicio que tiene un DAO inyectado y desea usar el servicio en el controlador, debe inyectarlo. La forma más fácil (IMO) es DI con Guice ... Además, puede tener sus dependencias en un solo lugar (módulo), etc ...


No estoy seguro si entiendo su pregunta, pero se prefiere la inyección porque:

  • diferentes partes de su aplicación están menos acopladas
  • es más fácil reemplazar su dependencia por una clase diferente que brinde la misma funcionalidad (en caso de que necesite hacer eso en el futuro) - necesitará cambiar algunas líneas de código y no buscar todas las ocurrencias de su objeto
  • es más fácil de probar (especialmente cuando necesitas burlarte de algo)

Hablando brevemente: D de los principios SÓLIDOS: "Depende de las abstracciones. No dependas de las concreciones".