Scala Cake Pattern Поощряют жестко закодированные зависимости? - PullRequest
18 голосов
/ 08 марта 2012

Я все еще пытаюсь выучить Шаблон Торта Скалы.Мне кажется, что это дает вам преимущество централизации вашей конфигурации «Компонентов», а также возможность предоставлять реализации по умолчанию для этих компонентов (которые, конечно, могут быть переопределены).

Однако, это использование selfчерты типа для описания зависимостей, кажется, смешивают проблемные области.Цель Компонента (я думаю) состоит в том, чтобы абстрагироваться от различных реализаций этого компонента.Но список зависимостей, описанный в Компоненте , сам по себе является проблемой реализации .

Например, допустим, у меня есть база данных, полная виджетов, реестр, который позволяет мне искать определенные видывиджетов и какой-то алгоритм, который использует реестр для обработки виджетов:

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 верхнего уровня.

Я что-то упустил?В тот момент, когда я использую собственный тип в любом из моих Компонентов, я заявляю, что все возможные реализации должны использовать те же самые зависимости.

Кто-нибудь еще сталкивался с этой проблемой?Как Cake-like способ ее решения?

Спасибо!

1 Ответ

17 голосов
/ 08 марта 2012

С шаблоном тортов, по крайней мере, в примере я всегда иду к , вы должны отделить определение интерфейса компонента от его реализации по умолчанию.Это четко отделяет зависимости интерфейса от зависимостей реализации.

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(_))
  }
}
...