Click here to Skip to main content
15,881,139 members
Articles / Programming Languages / Scala
Technical Blog

Scala : Dependency Injection / IoC

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
20 Nov 2015CPOL5 min read 10.9K  
I shall attempt to outline some of the ways you could do DI / IOC in Scala

In software engineering, dependency injection is a software design pattern that implements inversion of control for resolving dependencies. Dependency injection means giving an object its instance variables. Really. That’s it.

However there are several ways of doing this, and as such it is a fairly big topic, and I will not be able to go into the very specific details of DI/ IOC in one post.

Instead I shall attempt to outline some of the ways you could do DI / IOC in Scala (and like I say there are a few).

I will play nice though, and will try and point out good resources along the way, that you can follow for more information

Factories

One way of doing simple poor mans DI is to use factories, which decouple the client from the actual instance class that it may need to fulfill its role.

Here is an example of a factory and a class that needs a service inside it. We simply use the factory to get the service we need.

import com.typesafe.config.{ConfigObject, ConfigValue, ConfigFactory, Config}
import scala.collection.JavaConverters._
import java.net.URI
import java.util.Map.Entry
 
 
trait Processor {
  def Process() : Unit
}
 
class ActualProcessor() extends Processor {
  override def Process(): Unit = {
      println("ActualProcessor")
  }
}
 
 
object ProcessorFactory {
 
  var _processor: Processor = new ActualProcessor()
 
  // Getter
  def processor = _processor
 
  // Setter
  def processor_=(newProcessor: Processor): Unit = _processor = newProcessor
}
 
 
class OrderService {
 
  def processOrder(): Unit = {
    val processor = ProcessorFactory.processor
    processor.Process
  }
   
}
 
 
 
object ClassesDemo {
 
  def main(args: Array[String]) : Unit =
  {
    new OrderService().processOrder();
    System.in.read()
  }
}

Factories typically use static methods, such that they act as singletons and can be used from anywhere, and have a new instance set from anywhere (which is typically at the start of the app, or a test case)

Here is how we might change the factory to use a mock/test double before the testing starts. I am using ScalaTest in this example

package org.scalatest.examples.flatspec.beforeandafter
 
import org.scalatest._
 
 
class ExampleSpec extends FlatSpec with BeforeAndAfter {
 
  before {
    //for the tests we could use a Mock, or a Test double
    ProcessorFactory._processor = new MockProcessor()
  }
}

Google Guice

Google Guice is a DI library primarily for Java. However since Scala is a JVM language we may use it from Scala.

You can read more about Google Guice here : https://github.com/google/guice/wiki up on date 17/11/15

You willl need the following SBT libraryDependencies

"com.google.inject" % "guice" % "3.0"

Typical usage can be thought of as 4 separate things

  • Defining an abstraction, that our client code will depend on
  • Stating that the client code wants a dependency injected. This is done with annotations in Java/Scala using the @Inject annotation
  • Providing the wire up code to wire the abstraction that the client code wanted satisfied with the actual implementation instance that the client code will get at runtime
  • Get the item from the Google Guice DI framework

Let’s see an example of these 4 points

import com.google.inject.{ Inject, Module, Binder, Guice }
 
//The abstraction
trait Processor {
  def Process() : Unit
}
 
class ActualProcessor() extends Processor {
  override def Process(): Unit = {
      println("ActualProcessor")
  }
}
 
 
// OrderService needs a Processor abstraction
class OrderService @Inject()(processor : Processor) {
 
  def processOrder(): Unit = {
    processor.Process
  }
 
}
 
 
//Declare a Google guice module that provides the wire up code
class DependencyModule extends Module {
  def configure(binder: Binder) = {
    binder.bind(classOf[Processor]).to(classOf[ActualProcessor])
  }
}
 
 
object ClassesDemo {
 
  def main(args: Array[String]) : Unit =
  {
    //get the item from the DI framework
    val injector = Guice.createInjector(new DependencyModule)
    val orderService = injector.getInstance(classOf[OrderService])
    orderService.processOrder()
    System.in.read()
  }
}

This is a very very quick introduction to DI using Google Guice, but as you can see it is quite similar to other DI frameworks such as Spring (or Castle, Autofac, Unity in the .NET world). You should certainly read the wiki a bit more on this one.

MacWire

We will now spend a bit more time looking at another framework called “macwire” which you can read more about at this GitHub project :

https://github.com/adamw/macwire up on date 17/11/15

So how do we use this MacWire framework. Well to be honest it is not that different from Google Guice in the code you wrte, but it uses the idea of Scala Macros under the hood. Though you don’t really need to get involved with that to use it.

We need to include the following SBT libraryDependencies before we start

libraryDependencies ++= Seq(
  "com.softwaremill.macwire" %% "macros" % "2.1.0" % "provided",
  "com.softwaremill.macwire" %% "util" % "2.1.0",
  "com.softwaremill.macwire" %% "proxy" % "2.1.0"
)

So lets see an example usage shall we:

package com.barbersDemo
 
import com.softwaremill.macwire._
 
//The abstraction
trait Processor {
  def Process() : Unit
}
 
class ActualProcessor() extends Processor {
  override def Process(): Unit = {
      println("ActualProcessor")
  }
}
 
 
class MyApp {
  val processor = new ActualProcessor()
}
 
 
// OrderService needs a Processor abstraction
class OrderService(processor : Processor) {
 
  def processOrder(): Unit = {
    processor.Process
  }
 
}
 
object ClassesDemo {
 
  def main(args: Array[String]) : Unit =
  {
 
    // we would substitute this line for a line that loads a Test
    // module with a set of test services services instead if we
    // were interested in testing/mocking
    val wired = wiredInModule(new MyApp)
 
    val orderService = wired.wireClassInstance[OrderService](classOf[OrderService])
    orderService.processOrder()
    System.in.read()
  }
}

As you can see from a usability point of view, it is not that different from using Google Guice. What is different is that we DO NOT have to use the @Inject annotation 

Cake Pattern

The cake pattern for me is the hardest one to get out of the lot, but seems to be the defacto way of doing DI in Scala.

You do get used to it. I managed to do this without the internet to refer to with a colleague today, so it is something that comes with time.

So here is the example:

package com.barbersDemo
 
 
// This trait is how you would express a dependency
// Any class that needs a Processor would mix in this trait
// along with using a self type to allow us to mixin either
// a mock / test double
trait ProcessorComponent {
 
  //abstract implementation, inheritors provide implementation
  val processor : Processor
 
  trait Processor {
    def Process() : Unit
  }
}
 
 
// An actual Processor
trait ActualProcessorComponent extends ProcessorComponent {
 
  val processor = new ActualProcessor()
 
  class ActualProcessor() extends Processor {
    def Process(): Unit = {
      println("ActualProcessor")
    }
  }
}
 
 
// An test double Processor
trait TestProcessorComponent extends ProcessorComponent {
 
  val processor = new TestProcessor()
 
  class TestProcessor() extends Processor {
    def Process(): Unit = {
      println("TestProcessor")
    }
  }
}
 
 
 
// The service that needs the Processor dependency
// satisfied.Which happens via the use of mixins
// and the use of a self type
class OrderService {
 
  // NOTE : The self type that allows to
  // mixin and use a ProcessorComponent
  this: ProcessorComponent =>
 
  def ProcessOrder() {
    processor.Process()
  }
 
}
 
 
object ClassesDemo {
 
  def main(args: Array[String]) : Unit =
  {
    //val defaultOrderServiceComponent = new DefaultOrderServiceComponent with ActualProcessorComponent
 
    // To use the test double or mock we would use a line similar to this
    val defaultOrderServiceComponent = new OrderService with TestProcessorComponent
 
    defaultOrderServiceComponent.ProcessOrder()
    System.in.read()
  }
}

There are a couple of things to not there

  • We want to make use of a trait (abstract class) called “Processor” which others may extend to do something, or provide a mock/test implementation
  • We wrap the trait we want to inject in a xxxComponent (this appears to be some sort of convention), and we also have an abstract val that the inheritor of the trait will provide an implementation for. You can see this in the ProcessorComponent trait (which is abstract)
  • We then have an ActualProcessorComponent / TestProcessorComponent which implement the trait ProcessorComponent
  • The place where we want to make use of the service, we make use of the self type within the OrderService which is this part “this: ProcessorComponent =>”. What this really means is that the OrderService NEEDS a ProcessorComponent  mixed in to work correctly. But since we know we will have a ProcessorComponent  mixed in (eithe real implementation or mock / test double) we can make use of it in the OrderService class.
  • All that is left is to wire up the OrderService with either a real implementation or mock / test double. This is done in the ClassesDemo.main(..) method shown above

Some further “Cake Pattern” blogs

Structural Typing

The last example I wanted to look at was using structural typing. To my mind this is kind of like duck typing, if you are expecting something that has a Print method, and you get something that has a Print method you should be able to use it.

NOTE : this approach USES reflection so will have a performance impact if used a lot

Here is an example of using structural typing

package com.barbersDemo
 
import com.softwaremill.macwire._
 
//The abstraction
trait Processor {
  def Process() : Unit
}
 
class ActualProcessor() extends Processor {
  override def Process(): Unit = {
      println("ActualProcessor")
  }
}
 
 
class TestProcessor() extends Processor {
  override def Process(): Unit = {
    println("TestProcessor")
  }
}
 
 
 
// OrderService needs a Processor abstraction
// but this tim we use structural typing, if it looks like
// a duck and quakes like a duck its a duck kind of thing
class OrderService(env: { val processor: Processor }) {
 
  def processOrder(): Unit = {
    //this time we use the env parameter to obtain the dependency
    env.processor.Process
  }
 
}
 
 
 
object Config {
  lazy val processor = new ActualProcessor() // this is where injection happens
}
 
object TestConfig {
  lazy val processor = new TestProcessor() // this is where injection happens
}
 
object ClassesDemo {
 
  def main(args: Array[String]) : Unit =
  {
    new OrderService(Config).processOrder()
    new OrderService(TestConfig).processOrder()
    System.in.read()
  }
}

As this is a bit stranger I have included, a call which uses the actual implementation and also a call that uses a test implementation.

The good thing about this is there there is no extra libraries, it is all standard Scala, and it is immutable and type safe.

A nice way to go about things if you ask me

This article is part of the series 'Scala View All

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
-- There are no messages in this forum --