如何创建不需要显式构建方法的类型安全构建器?

try*_*ryx 1 scala builder

我使用了轻微滥用构建器模式来制作流畅的命令式执行链.我所追求的是一种使编译错误忘记最后的execute方法的方法.我的目标是如下

WithServiceA {
 doStuff()
} WithServiceB {
  doStuff()
} withClient client
Run Code Online (Sandbox Code Playgroud)

WithServiceA并且WithServiceB都可以返回值,因此如果使用返回值,很明显返回类型是错误的,但如果它们被强制使用,整个对象就会默默地落在地板上.我想确保忘记withClient调用是一个编译错误,无论它在什么上下文中使用.

我希望能够跳过块,如果它们是不需要的并按任意顺序放置,所以我希望替换我之前使用的嵌套内部类模式ala

def onServiceA[A](body: ServiceA => A) = new {   
  def onServiceB[B >: A](body: ServiceB => B) = {b => {
    doStuff()
  }
}
Run Code Online (Sandbox Code Playgroud)

sen*_*nia 5

它看起来像类型安全的构建器模式.看到这个答案.

在你的情况下:

trait TTrue
trait TFalse

class Myclass[TA, TB, TC] private(){
  def withServiceA(x: => Unit)(implicit e: TA =:= TFalse) = {x; new Myclass[TTrue, TB, TC]}
  def withServiceB(x: => Unit)(implicit e: TB =:= TFalse) = {x; new Myclass[TA, TTrue, TC]}
  def withServiceC(x: => Unit)(implicit e: TC =:= TFalse) = {x; new Myclass[TA, TB, TTrue]}
  def withClient(x: => Unit)(implicit e1: TA =:= TTrue, e2: TB =:= TTrue) = x
}

object Myclass{
  def apply() = new Myclass[TFalse, TFalse, TFalse]
}
Run Code Online (Sandbox Code Playgroud)

用法:

Myclass()
  .withClient(println("withClient"))
//<console>:22: error: Cannot prove that TFalse =:= TTrue.
//                .withClient(println("withClient"))
//                           ^


Myclass()
  .withServiceB(println("with B"))
  .withServiceA(println("with A"))
  .withClient(println("withClient"))
//with B
//with A
//withClient

Myclass()
  .withServiceA(println("with A"))
  .withServiceC(println("with C"))
  .withServiceB(println("with B"))
  .withClient(println("withClient"))
//with A
//with C
//with B
//withClient

Myclass()
  .withServiceC(println("with C"))
  .withServiceB(println("with B"))
  .withServiceA(println("with A"))
  .withServiceC(println("with C2"))
  .withClient(println("withClient"))
//<console>:25: error: Cannot prove that TTrue =:= TFalse.
//                .withServiceC(println("with C2"))
//                             ^
Run Code Online (Sandbox Code Playgroud)

您可以为=:=类提供自定义替换的自定义错误消息.

如果你想确保在每次Myclass.apply withClient调用之后,你可以像这样手动调用它:

sealed class Context private()
object Context {
   def withContext(f: Context => Myclass[TTrue, TTrue, _])(withClient: => Unit) =
     f(new Context).withClient(withClient)
}

object Myclass{
  def apply(c: Context) = new Myclass[TFalse, TFalse, TFalse]
}
Run Code Online (Sandbox Code Playgroud)

用法:

Context
  .withContext(
    Myclass(_)
      .withServiceA(println("with A"))
      .withServiceC(println("with C"))
      .withServiceB(println("with B"))
  )(println("withClient"))
Run Code Online (Sandbox Code Playgroud)

关于意识形态.

一个人不能MyclasswithContext方法之外创建,并且withClient至少会调用一次.