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:
- 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. EGCounter2.nextCount().toString
- Si tuviera que usar un
- Puede variar la implementación durante la prueba utilizando enlaces personalizados de Guice
- Digamos que dentro de
Counter
estás haciendo una llamadaWS
. 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 entreCounter
yAtomicCounter
para apuntar a una versión offline deCounter
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.
- Digamos que dentro de
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".