Dependencies Injection In Scala

Inspired By: Scala Central [Dave Gurnell] talk.

In software engineering, dependencies injection is the techniques of passing dependent objects or functions to the class, module or function for which they wanted to use. Dependencies injection inverse the flow of control of the program. It provides loose coupling and unit testing become easier since dependent objects can be mocked.

Dependencies injection motivates the programmer to write code as a component/unit and combining the other dependent objects to form feature/software

Lets consider the following example

object TemplateRenderer
{
  def apply(id: Template: params: Params) = ???
}

object TemplateLoader
{
  def apply(id: TemplateId) = ???
}

object GreetingCardService
{
  val loader = TemplateLoader
  val render = TemplateRenderer

  def greet(id: TemplateId, params: Params) =
    render(loader(id), params)
}

The two objects are TemplateRender and TemplateLoader are coupled into GreetingCardService object, if we wanted to test the above GreetingCardService then the test will be very slow and setting up the environment for the GreetingCarService will be tedious and time consuming. Following is the test case

"greeting service" should 
{
  "render a template" in
  {
    val params = Params(Map("name" -> "Scala User"))
    val actual = GreetingService.greet(template, params)
    val expected = "A testing template"
    actual should be(expected)
  }
}

 

There are dozens of dependencies injection in Scala. Some types of dependencies injection are Constructor, Google Guice, Macwire, Cake, XML, Function, and Reader etc.

Constructor Base Dependencies Injection:

We convert object to class and create a trait and extends it with the class of template loader. These are the dependencies which will be injected into GreetingService through constructor

trait TemplateLoader
{
  def apply(id: TemplateId): Template
}

class S3TemplateLoader extends TemplateLoader
{
  def apply(id: TemplateId): Template = ???
}

class FakeTemplateLoader extends TemplateLoader
{
  def apply(id: TemplateId): Template = ???
}

class GreetingService(
  loader: TemplateLoader, 
  render: TemplateRender
  )
{
  def greet(id: TemplateId, params: Param) = 
  render(loader(id), params)
}

To use the above Greeting service, we first create the instances of the dependencies

object App
{
  val loader = S3TemplateLoader()
  val render = CoacaTemplateRender()

  val greeting = new GreetinService(loader, render)
}

However, this code needs to first fulfill the pre-conditions resolving the dependencies for using the services.

Google Guice

The purpose of using Guice is to reduce the amount of wiring as in constructor DI. But the problem with the Guice is that it is runtime dependencies. It is very Java oriented and it does not know about companion objects.

import com.google.inject._

@ImplementedBy(classOf[S3TemplateLoader])
trait TemplateLoader { ... }

@ImplementedBy(classOf[CoacaTemplateRenderer])
trait TemplateRenderer { ... }

class GreetingService @Inject() (
  loader: TemplateLoader, render: TemplateRenderer)
{
  ...
}

object App
{
  val injectors = Guice.createInjector()
  val greeting = injectors.getInstance(classOf[GreetingService])
}

Macwire

This provides compile time dependencies injection. It will reduce the wiring code however, there can cause some problems when dependencies are a-cyclic.

Simple example for Macwire is follows

object App
{
  val loader = wire[S3TemplateLoader]
  val renderer = wire[CoacaTemplateRenderer]
  val greeting = wire[GreetingService]
}

 

Cake pattern

Converting classes to traits. This approach solves the wiring problem but creates the naming problem and in large application we will be out of naming things. Here, self type to resolving dependencies. Notice that the name is changed from apply to loader and render respectively.

trait TemplateLoader
{
  def loader(id: TemplateId): Template
}

trait TemplateRenderer
{
  def render(template: Template, params: Param)
}

trait GreetingService
{
  self: TemplateLoader with TemplateRenderer =>

  def greeting(id: TemplateId, param: Param) =
    render(loader(id), param)
}

When using the above cake pattern. Extend app with the dependent traits, and all the methods will be in scope

object App extends GreetingService
  with TemplateLoader
  with TemplateRenderer
  {
    App.greet(...)
  }

 

There are other terms and solutions such as Thin cake pattern and Full cake pattern however, does not solve the problem but it also create the wiring the problem and in my opinion encapsulation problem.

This pattern was introduced in Scala. But Martin Odersky has not recommend this (cake) pattern. It cause bakery of doom.

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: