Как вы делаете инъекцию зависимостей с шаблоном Cake без hardcoding?

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

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

Ответ 1

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

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

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

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

Но что, если вы хотите использовать другой 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

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

Ответ 2

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

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

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

не отличается от:

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

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

Ответ 3

Короткий ответ заключается в том, что Scala в настоящее время не имеет встроенной поддержки динамических микшингов.

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

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

Ответ 4

Пока плагин 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 внутри черты. Еще один недостаток заключается в том, что если в Module выше есть зависимые от пути типы, вы не сможете легко делегировать делегирование.

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