Kev*_*ith 3 dependency-injection scala
鉴于以下情况FooService:
scala> trait FooService {
| def go: Int
| }
defined trait FooService
Run Code Online (Sandbox Code Playgroud)
还有一个MainService,它代表一个 main 方法。
scala> trait MainService extends FooService {
| def f = go + 42
| }
defined trait MainService
Run Code Online (Sandbox Code Playgroud)
FooService可以有一个假的(用于测试)和一个真实的实现(例如,点击数据库):
scala> object FakeService extends FooService {
| def go = 10
| }
defined object FakeService
scala> object RealService extends FooService {
| def go = 55 // in reality, let's say it hit the DB and got a value
| }
defined object RealService
Run Code Online (Sandbox Code Playgroud)
在我看来,添加“跑步者”类/特征(即跑步sbt run将导致该类的执行)是可行的。它看起来像:
scala> class Main extends MainService {
| override def go = RealService.go
| }
defined class Main
Run Code Online (Sandbox Code Playgroud)
我也可以定义一个测试:
scala> class Test extends MainService {
| override def go = FakeService.go
| }
defined class Test
Run Code Online (Sandbox Code Playgroud)
我不太确定这是定义 real 与 test 的惯用方式MainService。请告诉我。
您可以使用流行的蛋糕模式,也称为执行依赖项注入的“Scala 方式”。
乔恩(Jon)写了一篇很棒的博客文章,其中包含演练(他还列出了一些替代方案)。
首先, 的特征FooService:
trait FooServiceComponent {
val fooService: FooService
trait FooService {
def go: Int
}
}
Run Code Online (Sandbox Code Playgroud)
也就是说,我们需要两件事:1. 实际对象,2. 它的定义/实现。两者命名空间在一起。好的。以下是Fake和Real版本:
trait FakeService extends FooServiceComponent {
class FakeService extends FooService {
def go = 10
}
}
trait RealService extends FooServiceComponent {
class RealService extends FooService {
def go = 55
}
}
Run Code Online (Sandbox Code Playgroud)
现在,对于MainService:
trait MainServiceComponent { this: FooServiceComponent =>
val mainService: MainService
class MainService extends FooService {
def f = go + 42
def go = fooService.go // using fooService
}
}
Run Code Online (Sandbox Code Playgroud)
请注意自键入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()
}
Run Code Online (Sandbox Code Playgroud)
请注意,由于命名空间的原因,FakeService无法访问它,Main因为它没有混合在一起。很好。:) 另请注意,您可以将类的任何实例化延迟到此时,这很方便,因为您可以轻松地使用注册表或模拟库将它们全部替换到一个位置。
结果:
println(Test.mainService.f) // -> 52
println(Main.mainService.f) // -> 97
Run Code Online (Sandbox Code Playgroud)
我希望这有帮助。