2016-05-17 69 views
21

Sto usando Play! Framework per Scala per quasi un anno. Attualmente sto usando la versione 2.5.x.Perché utilizzare @Singleton sull'oggetto Scala in Play Framework?

Sono a conoscenza dell'evoluzione dei controller in Play e di come gli sviluppatori sono stati costretti a rinunciare ai percorsi statici object.

Sono anche a conoscenza dell'utilizzo dello Guice in gioco.

Se si scarica e si esegue activator:

activator new my-test-app play-scala 

Activator produrrà un modello di progetto per voi. La mia domanda riguarda specificamente il file this di quel modello.

my-test-app/app/servizi/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() 
} 

Inoltre potrai vedere il suo utilizzo in this del file:

miei-test-app/app/controller /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) } 

} 

Ciò significa che ogni controllore che ha il costrutto oppure di @Inject() (counter: Counter) riceverà la stessa istanza di Counter.

Quindi la mia domanda è:

Perché usare @Singleton e poi @Inject in un controller, quando per questo esempio si potrebbe utilizzare un oggetto Scala?
È molto meno codice.

Esempio:

my-test-app/app/servizi/Counter.scala

package services 

trait ACounter { 
    def nextCount: Int 
} 

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

Usalo in questo modo:

my-test-app/app/controller/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) } 

} 

Qual è la differenza? L'iniezione è preferibile e perché?

+2

Probabilmente non importa se non è necessario passare i parametri al tuo controller, ma se lo fai, allora deve essere una classe per guiz per istanziare e iniettare le dipendenze. –

risposta

13

L'iniezione è preferibile? Generalmente sì

Un paio di vantaggi di utilizzare l'iniezione di dipendenza:

  1. regolatore Disaccoppiare dall'attuazione concreta di Counter.
    • Se si dovesse utilizzare un object, è necessario modificare il controller in modo che punti alla diversa implementazione.EG Counter2.nextCount().toString
  2. è possibile variare l'attuazione durante il test utilizzando Guice associazioni personalizzate
    • permette di dire che dentro di Counter si sta facendo una chiamata WS. Ciò potrebbe causare alcuni test dell'unità di difficoltà. Se si utilizza l'integrazione delle dipendenze con Guice, è possibile ignorare l'associazione tra Counter e AtomicCounter in modo che punti a una versione offline di Counter scritta in modo specifico per i test. Vedi here per maggiori informazioni sull'uso di Guice per i test di gioco.

vedere anche la motivations che il gioco ha avuto per la migrazione a DI.

Dico generalmente perché ho visto l'iniezione di dipendenza andare terribilmente male usando Spring e altri framework Java. Direi che dovresti usare il tuo giudizio, ma sbagliare sul lato di usare DI per Play.

3

Non sono sicuro, se ho capito la tua domanda, ma l'iniezione è preferibile perché:

  • diverse parti dell'applicazione sono meno accoppiati
  • è più facile sostituire la dipendenza con classe diversa che fornisce il stessa funzionalità (nel caso in cui avresti bisogno di farlo in futuro) - dovrai cambiare poche righe di codice e non cercare tutte le occorrenze del tuo oggetto
  • è più semplice da testare (soprattutto quando devi prendere in giro qualcosa)

In breve: D dai principi SOLID: "dipende dalle astrazioni. Non dipendere dalle concrezioni. "

4

Forse perché l'oggetto Singleton di Scala non può avere parametri? Ad esempio, se si ha una classe di servizio con DAO iniettato e si desidera utilizzare il servizio nel controller, è necessario iniettare Il modo più semplice (IMO) è DI con Guice ... Inoltre, puoi avere le tue dipendenze in un posto (modulo) ecc ...