避免与Cake Pattern名称冲突

par*_*tic 6 scala traits name-clash cake-pattern

我目前正在使用Cake Pattern来实现一些优化算法.我经常遇到名字碰撞问题.例如:

trait Add[T] { this: Foo[T] =>
  def constant: T
  def plus( t1: T, t2: T ): T
  def add( t: T ) = plus( t, constant )
}

trait Mul[T] { this: Bar[T] =>
  def constant: T
  def times( t1: T, t2: T ): T
  def mul( t: T ) = times( t, constant )
}

trait Operations[T] { this: Add[T] with Mul[T] =>
  def neg( t: T ): T
}
Run Code Online (Sandbox Code Playgroud)

这里,constant在两者AddMul特征中都有定义,但它们的值可能不同.我可以在名称前加上特征名称,但我发现它很丑陋(def mulConstant: T).有没有更好的方法呢?

小智 7

据我所知,传统的蛋糕模式通常涉及1层特性嵌套,以便将操作组合在一起.然后,外层声明实际的"服务"(这里:添加,Mul,操作)而不定义它.

trait AddComponent[T] { this: FooComponent[T] =>
  def addition: Add

  trait Add {
    def constant: T
    def plus( t1: T, t2: T ): T
    def add( t: T ) = plus( t, constant )
  }
}

trait MulComponent[T] { this: BarComponent[T] =>
  def multiplication: Mul

  trait Mul {
    def constant: T
    def times( t1: T, t2: T ): T
    def mul( t: T ) = times( t, constant )
  }
}

trait OperationsComponent[T] { this: Add[T] with Mul[T] =>
  def operations: Operations

  trait Operations {
    def neg( t: T ): T
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,当将"... Component"特征混合在一起时,依赖关系是有线的:

trait IntOperations extends Operation[Int] {
  class IntAdd extends Add { ... }
  class IntMul extends Mul { ... }
}

class MyFooBar extends FooComponent[Int] with BarComponent[Int] with IntOperations {
  lazy val addition = new IntAdd
  lazy val multiplication = new IntMul
  lazy val foo = ...
  lazy val bar = ...
}
Run Code Online (Sandbox Code Playgroud)

这解决了您的特定命名空间问题,但名称冲突("服务"定义)仍然是传统蛋糕模式的问题.Daniel Spiewak 发表一篇博客文章,演示了如何解决这一问题,但解决方案有其自身的一套(巨大的)权衡(参见本演讲).

希望有所帮助.

PS而不是类型参数在这里使用抽象类型可能更好