How to avoid dependency injection in Scala?

大憨熊 提交于 2019-12-03 17:03:58

Implicit parameters are completely sufficient for the use case you're describing.

case class A(implicit b: B, c: C)
case class B(implicit d: D)
case class C(implicit d: D)
class D { /* ... */ }

implicit val theD = new D
implicit val theB = B()
implicit val theC = C()

Now you can ask for an A just by:

val a = A()

You may solve it with self-types.

A depends on both B and C and both B and C depend on D

so one could write this like that:

class A {
  self: B with C => 
}

trait B { 
  self: D => 
}

trait C {
  self: D => 
}

trait D {}

and then on a call side:

val x = new A with BImpl with CImpl with DImpl

but code below won't compile, because dependencies on B,C,D classes not resolved:

val x = new A

It's tricky to provide that type of dependency injection. Most of the above examples require you to create the implicits near where the classes are instantiated.

Closest I could come up with is:

class A(implicit b:B, c:C)
class B(implicit d:D)
class C(implicit d:D)
trait D { //the interface 
  def x:Unit
}

object Implicits {
  implicit def aFactory:A = new A
  implicit lazy val bInstance:B = new B
  implicit def cFactory:C = new C
  implicit def dFactory:D = new D {
     def x:Unit = {/* some code */}
  }
}

And then in your code you use it like this:

import Implicits._

object MyApplication {
   def main(args: Array[String]):Unit = {
      val a = new A
   }
}

If you need to be able to specify different versions when you (for example) are testing, you could do something like this:

import Implicits._

object MyApplication {

  // Define the actual implicits
  Implicits.module = new Module {
    import Implicits._

    def a = new A
    lazy val b = new B
    def c = new C
    def d = new D {
      def x = println("x")
    }
  }

  def main(args: Array[String]):Unit = {
    val a = new A // or val a = implicitly[A] 
  }

}

// The contract (all elements that you need)
trait Module {
  def a: A
  def b: B
  def c: C
  def d: D
}

// Making the contract available as implicits
object Implicits {
  var module: Module = _

  implicit def aFactory:A = module.a
  implicit def bFactory:B = module.b
  implicit def cFactory:C = module.c
  implicit def dFactory:D = module.d
}

This would allow you to simply import Implicits._ in any file and would provide a similar workflow as the one in the original question.

In most cases however I would not use this tactic. I would simply make the implicit available in classes that create instances:

object MyApplication {

  implicit def a: A = new A
  implicit lazy val b: B = new B
  implicit def c: C = new C
  implicit def d: D = new D {
    def x: Unit = println("x")
  }

  def main(args: Array[String]): Unit = {
    val a = implicitly[A]
    val e = new E
  }

}

class E(implicit d:D) {
    new C
}

Here E is defined in another file and creates an instance of C. We require D to be passed to E and with that document that E depends on D (via C).

I think @om-nom-nom's answer is quite close to what you want. Here is what I've got:

class A {
  self: B with C => 

  def sum = tripleD + doubleD
}

trait B { 
  self: D => 

  def tripleD = x * 3
}

trait C {
  self: D => 

  def doubleD = x * 2
}

trait D extends B with C {
  val x: Int
}

trait E extends D {
  val x = 3
}

trait F extends D {
  val x = 4
}

val a = new A with E
val b = new A with F

println("a.sum = " + a.sum)
println("b.sum = " + b.sum)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!