Как вы делаете инъекцию зависимостей с шаблоном Cake без жесткого кодирования? - PullRequest
72 голосов
/ 02 марта 2011

Я только что прочитал и получил удовольствие Статья о торте Однако, на мой взгляд, одной из ключевых причин использования внедрения зависимостей является то, что вы можете изменять компоненты, используемые либо в XML-файле, либо в аргументах командной строки.

Как этот аспект DI обрабатывается с помощью шаблона Cake? Все примеры, которые я видел, связаны со смешиванием черт статически.

Ответы [ 5 ]

56 голосов
/ 02 марта 2011

Поскольку смешивание в чертах выполняется статически в Scala, если вы хотите изменить черты, смешанные с объектом, создайте различные объекты на основе некоторого условия.

Давайте рассмотрим пример канонического торта. Ваши модули определены как черты, а ваше приложение построено как простой объект с набором функциональных возможностей, смешанных в

val application =
    new Object
extends Communications
   with Parsing
   with Persistence
   with Logging
   with ProductionDataSource
application.startup

Теперь у всех этих модулей есть хорошие объявления собственных типов, которые определяют их межмодульные зависимости, так что строка компилируется только в том случае, если существуют все ваши межмодульные зависимости, уникальны и хорошо типизированы. В частности, модуль «Постоянство» имеет собственный тип, который говорит, что все, что реализует «Постоянство», должно также реализовывать DataSource, абстрактную черту модуля. Поскольку ProductionDataSource наследуется от DataSource, все отлично, и эта строка построения приложения компилируется.

Но что, если вы хотите использовать другой источник данных, указывающий на некоторую локальную базу данных для целей тестирования? Предположим далее, что вы не можете просто повторно использовать ProductionDataSource с другими параметрами конфигурации, загруженными из файла некоторых свойств. В этом случае вы должны определить новую черту TestDataSource, которая расширяет DataSource, и вместо этого смешать ее. Вы даже можете сделать это динамически, основываясь на флаге командной строки.

val application = if (test)
  new Object
    extends Communications
      with Parsing
      with Persistence
      with Logging
      with TestDataSource
else
  new Object
    extends Communications
      with Parsing
      with Persistence
      with Logging
      with ProductionDataSource

application.startup

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

29 голосов
/ 02 марта 2011

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

Просто посмотрите при запуске:

scala -cp first.jar:second.jar startupScript.scala

не так уж отличается от:

java -cp first.jar:second.jar com.example.MyMainClass context.xml

Вывсегда можно использовать DI, но у вас есть еще один инструмент.

5 голосов
/ 03 марта 2011

Короткий ответ: в Scala в настоящее время нет встроенной поддержки динамических миксинов.

Я работаю над плагином autoproxy для его поддержки, хотя в настоящее время он приостановлен до выпуска 2.9.когда у компилятора появятся новые функции, делающие его намного более легкой задачей.

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

3 голосов
/ 03 марта 2011

До тех пор, пока плагин AutoProxy не станет доступен, одним из способов достижения эффекта является использование делегирования:

trait Module {
  def foo: Int
}

trait DelegatedModule extends Module {
  var delegate: Module = _
  def foo = delegate.foo
}

class Impl extends Module {
  def foo = 1
}

// later
val composed: Module with ... with ... = new DelegatedModule with ... with ...
composed.delegate = choose() // choose is linear in the number of `Module` implementations

Но будьте осторожны, недостатком этого является то, что он более многословный, и вы должны быть осторожны спорядок инициализации, если вы используете var s внутри черты.Другим недостатком является то, что если в Module есть выше зависящие от пути типы, вы не сможете использовать делегирование так легко.

Но если существует большое количество различных реализаций, которые можно варьировать, этовероятно, будет стоить вам меньше кода, чем перечисление дел со всеми возможными комбинациями.

0 голосов
/ 02 марта 2011

В Lift встроено что-то в этом роде. В основном это код scala, но у вас есть некоторый контроль во время выполнения.http://www.assembla.com/wiki/show/liftweb/Dependency_Injection

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...