我在类型安全的构建者模式使用虚拟的类型,以确保方法被调用一次如下面的代码样本
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]
}
}
你在正确的轨道上,你只需要加一点点类型精化:
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
}