2015-07-04 336 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 

我希望這有助於。