在Scala中用另一个性状继承自型性状

在Scala中用另一个性状继承自型性状,scala,Scala,我正在尝试为我的应用程序设计一个小模块系统,这样我就可以做到: new MyApplication extends Module1 with Module2 ... with ModuleN 为了让我的模块在应用程序中注册,我还拥有: trait ModuleRegistry { def register(start: () => Unit) = // Stores start functions in a list } trait Module { self: ModuleR

我正在尝试为我的应用程序设计一个小模块系统,这样我就可以做到:

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 =>
}
这意味着一个模块的所有实现都必须使用注册表显式地自类型,而理想情况下,它们对注册表一无所知

因此,我的问题是:

  • 为什么编译器强迫我在扩展特性上重新指定这个自我类型?理想情况下,我甚至不希望扩展特性“看到”这种类型,因为它应该由基本模块特性处理。“具体”模块是由第三方实现的,因此向第三方公开注册表似乎有点难看
  • 有没有更好的方法来实现我在Scala中试图实现的目标,以便可以自由地混合模块

  • 这不是蛋糕模式的一个好用法,因为您希望所有模块都保留它们自己的功能(大概是这样),而不是在混合时相互混合和覆盖。如果你想要后者,你就不应该这样做:只有非常仔细的设计才能产生可预测和合理的结果,如果第三方应该提供模块,那么设计就不会那么仔细了

    相反,你应该坚持“偏好组合而非继承”的建议


    这是假设一个模块可以使用单例。如果您需要一个特定的实例,您可以在伴随对象中重写apply(语法为Module1()),或者添加Module1扩展的生成器特性(例如
    trait ModuleBuilder{def module:module}
    )。

    这比您想象的更糟糕。假设
    Module1
    Module2
    都定义
    start
    。。。。
    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"