J'utilise Play! Framework pour Scala depuis près d'un an maintenant. J'utilise actuellement la version 2.5.x .
Je suis conscient de l'évolution des contrôleurs dans Play et de la façon dont les développeurs ont été contraints de quitter les routes statiques object
.
Je suis également au courant de l'utilisation de Guice en jeu.
Si vous téléchargez activateur et exécutez:
activator new my-test-app play-scala
Activator produira un projet de modèle pour vous. Ma question concerne spécifiquement ce fichier de ce modèle.
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()
}
Vous pouvez également voir son utilisation dans le fichier this :
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) }
}
Cela signifie que chaque contrôleur qui a le constructeur de @Inject() (counter: Counter)
recevra la même instance de Counter
.
Ma question est donc:
Pourquoi utiliser @Singleton
Puis @Inject
Dans un contrôleur, alors que pour cet exemple, vous pouvez simplement utiliser un objet Scala?
C'est beaucoup moins de code.
Exemple:
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()
}
Utilisez-le comme ceci:
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) }
}
Quelle est la différence? L'injection est-elle préférée et pourquoi?
L'injection est-elle le moyen préféré? Généralement oui
Quelques avantages de l'utilisation de l'injection de dépendance:
Counter
. object
, vous devrez changer votre contrôleur pour pointer vers l'implémentation différente. EG Counter2.nextCount().toString
Counter
vous effectuez un appel WS
. Cela pourrait entraîner des difficultés de test unitaire. Si vous utilisez l'injection de dépendances avec Guice, vous pouvez remplacer la liaison entre Counter
et AtomicCounter
pour pointer vers une version hors ligne de Counter
que vous avez écrite spécifiquement pour vos tests. Voir ici pour plus d'informations sur l'utilisation des tests Guice for Play.Voir également les motivations que Play avait pour migrer vers DI.
Je dis généralement parce que j'ai vu l'injection de dépendances se tromper en utilisant Spring et d'autres frameworks Java. Je dirais que vous devriez utiliser votre propre jugement mais pécher par rapport à l'utilisation de DI pour Play.
Peut-être parce que l'objet singleton de Scala ne peut pas avoir de paramètres? Par exemple, si vous avez une classe de service sur laquelle un DAO est injecté et que vous souhaitez utiliser le service dans le contrôleur, vous devez l'injecter. Le moyen le plus simple (IMO) est DI avec Guice ... Aussi, vous pouvez avoir vos dépendances en un seul endroit (module) etc ...
Je ne suis pas sûr, si je comprends votre question, mais l'injection est préférable car:
En bref: D de SOLID principes: "Dépendre des abstractions. Ne pas dépendre des concrétions".