30

I have been using Play! Framework for Scala for nearly a year now. I am currently using version 2.5.x.

I am aware of the evolution of controllers in Play and how developers have been forced away from static object routes.

I am also aware of the Guice usage in play.

If you download activator and run:

activator new my-test-app play-scala

Activator will produce a template project for you. My question is specifically around this file of that template.

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

You can also see its usage in this file:

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

}

This means every controller which has the constructor of @Inject() (counter: Counter) will receive the same instance of Counter.

So my question is:

Why use @Singleton and then @Inject it into a controller, when for this example you could just use a Scala object?
Its a lot less code.

Example:

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

Use it like so:

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

}

What is the difference? Is injection the preferred, and why?

Rhys Bradbury
  • 1,699
  • 13
  • 24
  • 2
    It probably doesn't matter if you don't need to pass parameters to your controller. But if you do, then it needs to be a class for guice to instantiate and inject the dependencies. – Alvaro Carrasco May 17 '16 at 17:56

3 Answers3

20

Is injection the preferred way? Generally yes

A couple advantages of using dependency injection:

  1. Decouple controller from the concrete implementation of Counter.
    • If you were to use an object, you would have to change your controller to point to the different implementation. EG Counter2.nextCount().toString
  2. You can vary the implementation during testing using Guice custom bindings
    • Lets say that inside of Counter you are doing a WS call. This could cause some difficulty unit testing. If you are using dependency injection with Guice, you can override the binding between Counter and AtomicCounter to point to an offline version of Counter that you have written specifically for your tests. See here for more info on using Guice for Play tests.

Also see the motivations that Play had for migrating to DI.

I say generally because I've seen dependency injection go horribly wrong using Spring and other Java frameworks. I'd say you should use your own judgement but err on the side of using DI for Play.

nattyddubbs
  • 2,085
  • 15
  • 24
10

Maybe because Scala's singleton object can't have parameters? For example, if you have a service class that has a DAO injected, and you want to use service in controller, you have to inject it. The easiest way(IMO) is DI with Guice... Also, you can have your dependencies in one place(module) etc...

insan-e
  • 3,883
  • 3
  • 18
  • 43
4

I am not sure, if I understand your question, but injection is preferred because:

  • different parts of your application are less coupled
  • it is easier to replace your dependency with different class providing the same functionality (in case you would need to do that in future) - you will need to change few lines of code and not look for all occurrences of your object
  • it is simpler to test (especially when you need to mock something)

Shortly speaking: D from SOLID principles: "Depend upon Abstractions. Do not depend upon concretions".

michaJlS
  • 2,465
  • 1
  • 16
  • 22