5

我阅读了 Dependency Injection without the Gymnastics PDF,这表明不需要任何花哨的 DI 框架,但它超出了我的掌握范围(至少没有具体示例)。当我有机会时,我会尝试观看没有体操的依赖注入简单的依赖注入。

在 Java 中使用 Guice,如果 A 依赖于 B 和 C,而 B 和 C 都依赖于 D,则会有如下内容:

public class A {
    @Inject
    public A(B b, C c) {
        this.b = b;
        this.c = c;
    }
}

public class B {
    @Inject
    public B(D d) {
        this.d = d;
    }
}

public class C {
    @Inject
    public C(D d) {
        this.d = d;
    }
}

public class D { /* ... */ }

和一个说明要使用哪个 D 实现的模块,然后只需从注入器中请求 A 的一个实例:

A a = injector.createInstance(A.class);

鉴于上述 URL 中呈现的内容,上述代码的 Scala 等效项看起来如何?

FWIW,我也在调查https://github.com/dickwall/subcut/blob/master/GettingStarted.md,我只是想了解反 DI 解决方案。

4

4 回答 4

8

对于您描述的用例,隐式参数完全足够了。

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

现在您可以A通过以下方式要求:

val a = A()
于 2012-09-10T04:22:53.007 回答
4

你可以用self-types解决它。

A 依赖于 B 和 C 并且 B 和 C 都依赖于 D

所以可以这样写:

class A {
  self: B with C => 
}

trait B { 
  self: D => 
}

trait C {
  self: D => 
}

trait D {}

然后在通话方面:

val x = new A with BImpl with CImpl with DImpl

但下面的代码不会编译,因为对 B、C、D 类的依赖没有解决:

val x = new A
于 2012-09-09T18:58:49.483 回答
1

提供这种类型的依赖注入很棘手。上述大多数示例都要求您在实例化类的位置附近创建隐式。

我能想到的最接近的是:

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 */}
  }
}

然后在你的代码中你像这样使用它:

import Implicits._

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

如果您(例如)在测试时需要能够指定不同的版本,您可以执行以下操作:

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
}

这将允许您在任何文件中简单地导入 Implicits._ 并提供与原始问题中的工作流程类似的工作流程。

然而,在大多数情况下,我不会使用这种策略。我只会在创建实例的类中提供隐式:

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
}

这里E是在另一个文件中定义并创建一个C. 我们需要D传递给依赖于(via )E的文档并与其一起传递。EDC

于 2012-12-13T22:30:43.873 回答
0

我认为@om-nom-nom 的答案非常接近您想要的。这是我所拥有的:

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)
于 2012-09-11T02:05:56.280 回答