2013-01-11 50 views
2

我使用類型安全的構建者模式幻象類型,以確保方法被調用一次如下面的代碼樣本中類型安全生成器:如何

sealed trait TBoolean 
    sealed trait TTrue extends TBoolean 
    sealed trait TFalse extends TBoolean 

    class Builder[MethodCalled <: TBoolean] private() { 

    def foo()(implicit ev: MethodCalled =:= TFalse): Builder[TTrue] = { 
     new Builder[TTrue] 
    } 
    } 

    object Builder { 
    def apply() = new Builder[TFalse]() 
    } 

我很欣賞這種做法幻象類型組合因爲可以使用. - 運算符來鏈方法調用(不像其他方法) 然而,這是否有很多方法來保護的東西,如

class Builder[MethodCalled1 <: TBoolean, MethodCalled2 <: TBoolean, ... ,MethodCalledN <: TBoolean] 

結束變得不方便有沒有一種方法來創建一個「類型結構「?類似下面的僞代碼:

type S { 
     type MethodCalled1 <: TBoolean 
     type MethodCalled2 <: TBoolean 
     ... 
     type MethodCalledN <: TBoolean 
    } 

    class Builder[S] private() { 

    def foo()(implicit ev: S#MethodCalled1 =:= TFalse): Builder[S#MethodCalled1.TTrue] = { 
     new Builder[S#MethodCalled1.TTrue] 
    } 
    } 
+0

我不知道我完全理解這個問題,但有你嘗試過類似於特質S {type MethodCalled1 <:TBoolean; ... type MethodCalledN <:TBoolean}',而不是有一個模板類'Builder [S]',你有一個類'Builder(s:S)'並且引用像's.MethodCalled1'這樣的內部類型? – Kane

回答

5

你在正確的軌道上,你只需要加一點點類型精:

trait BuilderMethods { 
    type FooCalled <: TBoolean 
    type BarCalled <: TBoolean 
} 

class Builder[M <: BuilderMethods] private() { 
    def foo()(implicit ev: M#FooCalled =:= TFalse): Builder[M {type FooCalled = TTrue}] = { 
    new Builder[M {type FooCalled = TTrue}] 
    } 
    def bar()(implicit ev: M#BarCalled =:= TFalse): Builder[M {type BarCalled = TTrue}] = { 
    new Builder[M {type BarCalled = TTrue}] 
    } 
} 

object Builder { 
    type UnusedBuilder = BuilderMethods {type FooCalled = TFalse; type BarCalled = TFalse;} 
    def apply(): Builder[Builder.UnusedBuilder] = new Builder[UnusedBuilder]() 
} 

object TestPhantomStruct extends App { 
    val newBuilder = Builder() 
    val builderFooCalled = newBuilder.foo() 
    val builderFooCalledTwice = builderFooCalled.foo() // will not compile 
    val builderFooCalledBarCalled = builderFooCalled.bar() 
    val builderFooCalledTwiceBarCalled = builderFooCalledBarCalled.foo() // will not compile 

    val builderBarCalled = newBuilder.bar() 
    val builderBarCalledTwice = builderBarCalled.bar() // will not compile 
}