5

我需要一种智能的组件组合机制,它允许混合特征在组合组件之后进行初始化。以下抛出一个NullPointerException

class Component {
  def addListener(pf: PartialFunction[Any, Unit]) {}
}

trait DynamicComponent {
  protected def component: Component

  component.addListener {
    case x =>
  }
}

class Foo extends DynamicComponent {
  protected val component = new Component
}

new Foo  // -> NullPointerException

以下事情不是我的选择:

  • 使用protected lazy val component; 这会产生大量需要变得懒惰的 val,这是不想要的。
  • 放入addListener一个方法,例如initDynamic();因为我将混合许多特征,而且我不想记得调用六种initFoo()方法。
  • 使用DelayedInit. 至少根据 scaladocs,这不适用于特征。

我可以接受一个init()电话,但仅限于以下条件:

  • 所有混合的特征都可以很容易地声明在这个单一的调用中被调用
  • 忘记该init()语句是编译错误。
4

4 回答 4

10

您可以通过使用早期定义来延迟特征的初始化。(参见scala 语言规范的第 5.1.6 节)

class Foo extends {
  protected val component = new Component
} with DynamicComponent
于 2013-04-27T11:24:53.410 回答
2

它甚至比您的解决方案更笨重,但您始终可以要求创建一个必须使用该init()方法设置的 val。您可以选择不最后执行并在运行时出错,但至少您不会完全忘记它:

class Component {
  def addListener(pf: PartialFunction[Any, Unit]) {
    println("Added")
  }
}

trait Dyn {
  protected def component: Component
  protected val initialized: Init
  class Init private () {}
  private object Init { def apply() = new Init() }
  def init() = { component.addListener{ case x => }; Init() }
}

class Foo extends Dyn {
  protected val component = new Component
  protected val initialized = init()
}

别作弊!:

> class Bar extends Dyn { protected val component = new Component }
<console>:12: error: class Bar needs to be abstract, since value
initialized in trait Dyn of type Bar.this.Init is not defined
       class Bar extends Dyn { protected val component = new Component }

这样做的好处是,如果您需要在协作初始化所有东西之前准备好多个东西,或者如果您的Component类是final这样,您就不能混合其他任何东西。

于 2013-04-27T15:43:34.413 回答
1

一个想法可能是使用此处描述的技巧: 蛋糕模式:如何获取组件提供的所有 UserService 类型的对象

您应该初始化的所有组件都可以在一些Seq[InitializableComponent]. 然后您可以使用 foreach 初始化所有已注册的组件。

该 Seq 中不会忘记任何组件,因为它们是自动注册的,但是您仍然可以忘记调用 foreach...

于 2013-05-01T10:48:14.203 回答
0

这是一个想法(我很高兴阅读其他建议):

class Component {
  def addListener(pf: PartialFunction[Any, Unit]) {
    println("Added")
  }
}  

trait DynamicComponentHost {
  protected def component: Component with DynamicPeer

  protected trait DynamicPeer {
    _: Component =>
    addListener {
      case x =>
    }
  }
}

class Foo extends DynamicComponentHost {
  protected val component = new Component with DynamicPeer
}

new Foo

所以基本上我强迫组件混合只能由混合特征提供的类型。合理的?在我看来有点太复杂了。

于 2013-04-27T11:24:49.550 回答