3
votes

I was searching a way of doing dependency injection in Scala kind of like Spring or Unity in C# and I found nothing really interesting.

  • MacWire: I don't understand the benefit as we have to give the class in wire[CASS]. So what's the point if you give the implementation when you call wire? I can do new CASS it will be the same.
  • Cake pattern with self type: Seems to not answer what I'm searching for.

So I decided to make my implementation and ask you what do you think because it's surprising me that nothing like this has been done before. Maybe my implementation have lot's of issues in real life also.

So here is an example:

trait Messenger {
  def send
}

class SkypeMessenger extends Messenger {
  def send = println("Skype")
}

class ViberMessenger extends Messenger {
  def send = println("Viber")
}

I want here to inject everywhere in my app the implementation configured in only one place:

object App {
  val messenger = Inject[Messenger]

  def main(args: Array[String]) {
    messenger.send
  }
}

Note the Inject[Messenger] that I define like below with the config I want (prod or dev):

object Inject extends Injector with DevConfig

trait ProdConfig {
  this: Injector =>
  register[Messager](new SkypeMessager)
  register[Messager](new ViberMessager, "viber")
}

trait DevConfig {
  this: Injector =>
  register[Messager](new ViberMessager)
  register[Messager](new ViberMessager, "viber")
}

And finally here is the Injector which contains all methods apply and register:

class Injector {
  var map = Map[String, Any]()

  def apply[T: ClassTag] =
    map(classTag[T].toString).asInstanceOf[T]

  def apply[T: ClassTag](id: String) =
    map(classTag[T].toString + id).asInstanceOf[T]

  def register[T: ClassTag](instance: T, id: String = "") = {
    map += (classTag[T].toString + id -> instance)
    instance
  }
}

To summaries:

  • I have a class Injector which is a Map between interfaces/traits (eventually also an id) and an instance of the implementation.
  • We define a trait for each config (dev, prod...) which contains the registers. It also have a self reference to Injector.
  • And we create an instance of the Injector with the Config we want
  • The usage is to call the apply method giving the Interface type (eventually also an id) and it will return the implementation's instance.

What do you think?

3
I think it would help me if you provided an example of why the existing approaches were deficient when applied to your use case. The question is too abstract for me to give feedback at the moment. - Dave Swartz
It is not obvious how your implementation will resolve inner dependencies. Also take a look at subcut it has (almost) everything you need for runtime DI in scala. - vitalii

3 Answers

2
votes

You code looks a lot like dependency injection in Lift web framework. You can consult Lift source code to see how it's implemented or just use the framework. You don't have to run a Lift app to use its libraries. Here is a small intro doc. Basically you should be looking at this code in Lift:

package net.liftweb.http

/**
 * A base trait for a Factory.  A Factory is both an Injector and
 * a collection of FactorMaker instances.  The FactoryMaker instances auto-register
 * with the Injector.  This provides both concrete Maker/Vender functionality as
 * well as Injector functionality.
 */
trait Factory extends SimpleInjector

You can also check this related question: Scala - write unit tests for objects/singletons that extends a trait/class with DB connection where I show how Lift injector is used.

0
votes

Thanks guys,

So I make my answer but the one from Aleksey was very good.

I understand better the Cake Pattern with this sample:
https://github.com/freekh/play-slick/tree/master/samples/play-slick-cake-sample
Take a look also to the other implementations without DI and compare:
https://github.com/freekh/play-slick/tree/master/samples/

And so the cake pattern doesn't have a centralized config like we can have with my shown lift style DI. I will anyway use the Cake pattern as it fits well with Slick.
What I didn't like with Subcut is the implicits everywhere. I know there is a way to avoid them but it looks like a fix to me.

Thanks

0
votes

To comment on MacWire, you are right that you could just use new - and that's the whole point :). MacWire is there only to let you remove some boilerplate from your code, by not having to enumerate all the dependencies again (which is already done in the constructor).

The main idea is that you do the wiring at "the end of the world", where you assemble your application (or you could divide that into trait-modules, but that's optional). Otherwise you just use constructors to express dependencies. No magic, no frameworks.