Pourquoi utiliser @Singleton sur L'objet de Scala dans Play Framework?

j'ai utilisé Jouer! Framework 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 en jeu et de la façon dont les développeurs ont été forcés de s'éloigner de la statique object itinéraires.

je suis également conscient de l' Guice utilisation en jeu.

Si vous téléchargez activateur et exécuter:

activator new my-test-app play-scala

activateur produira un projet de modèle pour vous. Ma question est spécifiquement autour de fichier de ce modèle.

mon-test-app/app/services/Compteur.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 aussi voir son utilisation dans fichier:

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 Chaque contrôleur qui a le constructeur de @Inject() (counter: Counter) recevrez la même instance de Counter.

alors ma question Est:

Pourquoi utiliser @Singleton et @Inject dans un contrôleur, quand dans cet exemple vous pouvez juste utiliser un objet Scala?

Son beaucoup moins de code.

Exemple:

mon-test-app/app/services/Compteur.scala

package services

trait ACounter {
  def nextCount: Int
}

object Counter with ACounter {
  private val atomicCounter = new AtomicInteger()
  def nextCount(): Int = atomicCounter.getAndIncrement()
}

Utiliser comme donc:

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érable, et pourquoi?

26
demandé sur Rhys Bradbury 2016-05-17 20:29:02

3 réponses

l'injection est-elle la méthode préférée? Généralement oui

quelques avantages de l'injection de dépendances:

  1. découpler le contrôleur de la mise en œuvre concrète de Counter.
    • si vous utilisez un object, Vous devriez changer votre controller pour pointer vers l'implémentation différente. Par exemple Counter2.nextCount().toString
  2. vous pouvez modifier l'implémentation pendant les tests en utilisant des fixations personnalisées de Guice
    • Permet de dire qu'à l'intérieur de Counter tu fais un WS appel. Cela pourrait causer des difficultés dans les tests unitaires. Si vous utilisez l'injection de dépendances avec Guice, vous pouvez annuler la liaison entre Counter et AtomicCounter pour pointer vers une version hors ligne de Counter que vous avez écrit spécifiquement pour vos tests. Voir ici pour plus d'informations sur L'utilisation de Guice pour les tests Play.

voir Aussi motivations que le Jeu avait pour la migration pour DI.

je dis généralement parce que j'ai vu l'injection de dépendances se passer horriblement mal en utilisant Spring et d'autres cadres Java. Je dirais que vous devriez utiliser votre propre jugement mais errer du côté de L'utilisation de DI pour jouer.

17
répondu nattyddubbs 2018-03-15 13:26:41

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 qui a un DAO injecté, et que vous voulez utiliser service dans controller, vous devez l'injecter. La manière la plus facile(IMO) est DI avec Guice... En outre, vous pouvez avoir vos dépendances dans un endroit(module) etc...

5
répondu insan-e 2016-05-17 22:04:25

je ne suis pas sûr, si je comprends bien votre question, mais l'injection est préféré parce que:

  • les différentes parties de votre application sont moins couplée
  • il est plus facile de remplacer votre dépendance par une classe différente offrant la même fonctionnalité (au cas où vous auriez besoin de le faire à l'avenir) - vous devrez changer quelques lignes de code et ne pas chercher toutes les occurrences de votre objet
  • il est plus simple de tester (surtout quand vous devez vous moquer quelque chose)

bref: D des principes solides: "dépendent des Abstractions. Ne dépendent pas de concrétions".

4
répondu michaJlS 2016-05-17 19:00:09