18

私はまだScalaのケーキパターンを学ぼうとしています。「コンポーネント」の構成を一元化できるという利点と、それらのコンポーネントのデフォルトの実装を提供できるという利点があるように思われます(もちろんオーバーライド可能です)。

ただし、依存関係を説明するための自己タイプの特性の使用は、懸念領域が混在しているようです。コンポーネントの目的(私は思う)は、そのコンポーネントのさまざまな実装を抽象化することです。ただし、コンポーネントで説明されている依存関係リストは、それ自体が実装上の懸念事項です。

たとえば、ウィジェットでいっぱいのデータベース、特定の種類のウィジェットを検索できるレジストリ、およびレジストリを使用してウィジェットを処理するある種のアルゴリズムがあるとします。

case class Widget(id: Int, name:String)

trait DatabaseComponent {
  def database: (Int => Widget) = new DefaultDatabase()

  class DefaultDatabase extends (Int => Widget) {
    // silly impl
    def apply(x: Int) = new Person(x, "Bob")
  }
}

trait RegistryComponent {
  this: DatabaseComponent =>  // registry depends on the database

  def registry: (List[Int] => List[Widget]) = new DefaultRegistry()

  class DefaultRegistry extends (List[Int] => List[Widget]) {
    def apply(xs: List[Int]) = xs.map(database(_))
  }
}

trait AlgorithmComponent {
  this: RegistryComponent =>  // algorithm depends on the registry

  def algorithm: (() => List[Widget]) = new DefaultAlgorithm()

  class DefaultAlgorithm extends (() => List[Widget]) {
    // look up employee id's somehow, then feed them
    // to the registry for lookup
    def apply: List[Widget] = registry(List(1,2,3))
  }
}

そして今、あなたはそれをいくつかの中央構成にまとめることができます:

object Main {
  def main(args: Array[String]) {
    val algorithm = new AlgorithmComponent() with RegistryComponent with DatabaseComponent

    val widgets = println("results: " + algorithm.processor().mkString(", "))
  }
}

別のデータベースに変更したい場合は、ミックスインを変更することで簡単に挿入できます。

val algorithm = new AlgorithmComponent() with RegistryComponent with SomeOtherDatabaseComponent


しかし...データベースを使用しない 別のレジストリコンポーネントを混在させたい場合はどうなりますか?

別の(デフォルト以外の)実装でRegistryComponentをサブクラス化しようとすると、RegistryComponentはDatabaseComponentの依存関係を含めるように要求します。そして、RegistryComponentを使用する必要があります。これは、トップレベルのAlgorithmComponentが必要とするものだからです。

私は何かが足りないのですか?コンポーネントのいずれかでセルフタイプを使用する瞬間、すべての可能な実装で同じ依存関係を使用する必要があることを宣言しています。

他の誰かがこの問題に遭遇しましたか?それを解決するケーキのような方法は何ですか?

ありがとう!

4

1 に答える 1

17

ケーキパターンでは、少なくとも私がいつも行っている例では、コンポーネントのインターフェイス定義をデフォルトの実装から分離する必要があります。これにより、インターフェースの依存関係が実装の依存関係から明確に分離されます。

trait RegistryComponent {
  // no dependencies
  def registry: (List[Int] => List[Widget])
}

trait DefaultRegistryComponent extends RegistryComponent {
  this: DatabaseComponent =>  // default registry depends on the database

  def registry: (List[Int] => List[Widget]) = new DefaultRegistry()

  class DefaultRegistry extends (List[Int] => List[Widget]) {
    def apply(xs: List[Int]) = xs.map(database(_))
  }
}
于 2012-03-08T17:22:19.063 に答える