在Scala中继承自我类型特征与另一个特征

时间:2015-07-19 21:18:51

标签: scala

我试图为我的应用程序设计一个小模块系统,以便我可以这样做:

new MyApplication extends Module1 with Module2 ... with ModuleN

为了让我的模块在应用程序中注册自己,我也有:

trait ModuleRegistry {
  def register(start: () => Unit) = // Stores start functions in a list
}

trait Module {
  self: ModuleRegistry =>
  self.register(start)
  def start(): Unit
}

class Application extends ModuleRegistry {
}

trait Module1 extends Module {
  ...
}

这个想法是模块可以在注册表中注册一个函数,以便在应用程序启动时调用。不幸的是,Scala编译器迫使我这样做:

trait Module1 extends Module {
  self: ModuleRegistry =>
}

意味着模块的所有实现都必须使用注册表显式自我键入,理想情况下,他们不了解它。

所以我的问题是:

  1. 为什么编译器强迫我在扩展特征上重新指定这个自我类型?理想情况下,我甚至不希望延伸特征能够看到'这种类型,因为它应该由基本模块特征处理。 '具体'模块意在由第三方实施,因此将注册表暴露给它们似乎有点难看。
  2. 有没有更好的方法来实现我在Scala中尝试实现的功能,以便模块可以自由混合?

1 个答案:

答案 0 :(得分:6)

这不是对蛋糕模式的好用,因为你希望所有模块保留它们自己的功能(大概),混合时不要混合和覆盖。如果你想要后者,你不应该:只有非常仔细的设计才能提供可预测和合理的结果,如果第三方应该提供模块,那么设计将非常保证 小心。

相反,你应该坚持“赞成组合而不是继承”的建议。

trait Module {
  def start: Unit
}

trait Modular {
  protected def moduleList: Seq[Module]
  protected def register() = moduleList.map(m => m.start _)
}

class Application(modules: Module*) extends Modular {
  protected def moduleList: Seq[Module] = modules  // Fix varargs type
  protected val registered = register()
}

object Module1 extends Module {
  def start = println("One")
}

object Module2 extends Module {
  def start = println("Two")
}

val app = new Application(Module1, Module2) {
 registered.foreach(_())
}
// Prints "One", then "Two"

这假设单个模块可以正常使用。如果您需要特定实例,则可以在配对对象中覆盖apply(语法为Module1()),或者添加Module1扩展的构建器特征(例如trait ModuleBuilder { def module: Module })。