Scala 特征的依赖注入`

Scala 特征的依赖注入`,scala,dependency-injection,Scala,Dependency Injection,给定以下FooService: scala> trait FooService { | def go: Int | } defined trait FooService trait FooServiceComponent { val fooService: FooService trait FooService { def go: Int } } trait MainServiceComponent { this: FooService

给定以下
FooService

scala> trait FooService { 
     |   def go: Int 
     | }
defined trait FooService
trait FooServiceComponent {
  val fooService: FooService

  trait FooService {
    def go: Int
  }
}
trait MainServiceComponent { this: FooServiceComponent =>
  val mainService: MainService

  class MainService extends FooService {
    def f = go + 42
    def go = fooService.go // using fooService
  }
}
还有一个
MainService
,它代表一个main方法

scala> trait MainService extends FooService { 
     |   def f = go + 42
     | }
defined trait MainService
FooService
可以有一个假的(用于测试)和一个真实的实现(例如hits DB):

在我看来,添加一个“runner”类/特征,即运行
sbt run
将导致该类的执行是可行的。它看起来像:

scala> class Main extends MainService {
     |   override def go = RealService.go
     | }
defined class Main
我也可以定义一个测试:

scala> class Test extends MainService {
     |   override def go = FakeService.go
     | }
defined class Test

我不太确定这是否是定义真实与测试
MainService
的惯用方法。请让我知道。

您可以使用流行的cake模式,也被称为依赖项注入的“Scala方式”

Jon在这方面做了大量的演练(他还列出了一些备选方案)

首先,
FooService
的特性:

scala> trait FooService { 
     |   def go: Int 
     | }
defined trait FooService
trait FooServiceComponent {
  val fooService: FooService

  trait FooService {
    def go: Int
  }
}
trait MainServiceComponent { this: FooServiceComponent =>
  val mainService: MainService

  class MainService extends FooService {
    def f = go + 42
    def go = fooService.go // using fooService
  }
}
也就是说,我们需要两样东西:1。实际对象,以及2。其定义/实施。两个名称空间都在一起。美好的以下是
版本:

trait FakeService extends FooServiceComponent {
  class FakeService extends FooService {
    def go = 10
  }
}

trait RealService extends FooServiceComponent {
  class RealService extends FooService {
    def go = 55
  }
}
现在,对于
main服务

scala> trait FooService { 
     |   def go: Int 
     | }
defined trait FooService
trait FooServiceComponent {
  val fooService: FooService

  trait FooService {
    def go: Int
  }
}
trait MainServiceComponent { this: FooServiceComponent =>
  val mainService: MainService

  class MainService extends FooService {
    def f = go + 42
    def go = fooService.go // using fooService
  }
}
注意自键入
this:FooServiceComponent
,这是一种Scala方式,表示
MainServiceComponent
依赖于
FooServiceComponent
。如果您尝试实例化
MainServiceComponent
,而不混入任何
FooServiceComponent
,则会出现编译时错误。不错。:)

现在,让我们创建具有不同特征的
Test
Main
对象:

object Test extends MainServiceComponent with FakeService {
  val mainService = new MainService()
  val fooService = new FakeService()
}

object Main extends MainServiceComponent with RealService {
  val mainService = new MainService()
  val fooService = new RealService()
}
请注意,由于名称空间的原因,
FakeService
无法在
Main
中访问,因为它没有混入。不错。:)还要注意的是,您将类的任何实例化延迟到这一点,这很方便,因为您可以轻松地使用注册表或模拟库在一个地方替换它们

结果:

println(Test.mainService.f) // -> 52
println(Main.mainService.f) // -> 97

我希望这有帮助。

您可以从MainService扩展FakeService/RealService并使用它们。或者你可以制作FakeService/RealService traits并将它们混合到Main/Test中。我发现这是一个很好的开始。