2015-07-04 400 views
3

鉴于以下FooService依赖注入`trait`

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

还有一个MainService,它代表一个主要方法。

scala> trait MainService extends FooService { 
    | def f = go + 42 
    | } 
defined trait MainService 

FooService可以有一个假的(测试)和一个真正的实现(打DB为例):

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 

在我看来,添加一个“亚军”类/特性,即运行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的惯用方式。请告诉我。

+2

您可以从MainService扩展FakeService/RealService并使用它们。或者你可以制作FakeService/RealService特性并将它们混合到Main/Test。 –

+0

查看Google Guice的简单易用的DI解决方案。我发现这是很好的开始。 –

回答

4

您可以使用俗称的“Scala方式”进行依赖注入的流行蛋糕模式。

乔恩做了一个伟大的blog post关于此与演练(他列出了一些替代品)。

首先,对于FooService性状:

trait FooServiceComponent { 
    val fooService: FooService 

    trait FooService { 
    def go: Int 
    } 
} 

这是说,我们需要两样东西:1,实际的对象,并且2.它的定义/实现。两个名字一起放在一起。尼斯。下面是FakeReal版本:现在

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

trait RealService extends FooServiceComponent { 
    class RealService extends FooService { 
    def go = 55 
    } 
} 

,为MainService

trait MainServiceComponent { this: FooServiceComponent => 
    val mainService: MainService 

    class MainService extends FooService { 
    def f = go + 42 
    def go = fooService.go // using fooService 
    } 
} 

注意自我打字this: FooServiceComponent这是说,MainServiceComponentFooServiceComponent依赖的斯卡拉方式。如果您尝试在没有混合FooServiceComponent的情况下实例化MainServiceComponent,那么您将收到编译时错误。尼斯。 :)

现在,让我们创建TestMain对象的不同特点:

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 

我希望这有助于。