我试图为我的应用程序设计一个小模块系统,以便我可以这样做:
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 =>
}
意味着模块的所有实现都必须使用注册表显式自我键入,理想情况下,他们不了解它。
所以我的问题是:
答案 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 }
)。