Я попробовал использовать шаблон торта в своем проекте, и он мне очень понравился, но есть одна проблема, которая меня беспокоит.
Шаблон торта прост в использовании, когда все ваши компоненты имеют одинаковое время жизни. Вы просто определяете несколько трейтов-компонентов, расширяете их с помощью трейтов-реализации, а затем объединяете эти реализации в одном объекте, и через самотипы все зависимости автоматически разрешаются.
Но предположим, что у вас есть компонент (со своими зависимостями), который может быть создан в результате действий пользователя. Этот компонент нельзя создать при запуске приложения, так как для него еще нет данных, но он должен иметь автоматическое разрешение зависимостей при создании. Примером взаимосвязи таких компонентов является главное окно GUI и его сложные подэлементы (например, вкладка в панели записной книжки), которые создаются по запросу пользователя. Главное окно создается при запуске приложения, а некоторая подпанель в нем создается при выполнении пользователем какого-либо действия.
Это легко сделать в DI-фреймворках, таких как Guice: если мне нужно несколько экземпляров некоторого класса, я просто ввожу Provider<MyClass>
; затем я вызываю метод get()
для этого провайдера, и все зависимости MyClass
разрешаются автоматически. Если MyClass
требуются некоторые динамически вычисляемые данные, я могу использовать вспомогательное расширение для внедрения, но результирующий код по-прежнему сводится к поставщику/фабрике. Связанная концепция, области действия, также помогает.
Но я не могу придумать хороший способ сделать это, используя шаблон торта. В настоящее время я использую что-то вроде этого:
trait ModelContainerComponent { // Globally scoped dependency
def model: Model
}
trait SubpaneViewComponent { // A part of dynamically created cake
...
}
trait SubpaneControllerComponent { // Another part of dynamically created cake
...
}
trait DefaultSubpaneViewComponent { // Implementation
self: SubpaneControllerComponent with ModelContainerComponent =>
...
}
trait DefaultSubpaneControllerComponent { // Implementation
self: SubpaneViewComponent with ModelContainerComponent =>
...
}
trait SubpaneProvider { // A component which aids in dynamic subpane creation
def newSubpane(): Subpane
}
object SubpaneProvider {
type Subpane = SubpaneControllerComponent with SubpaneViewComponent
}
trait DefaultSubpaneProvider { // Provider component implementation
self: ModelContainerComponent =>
def newSubpane() = new DefaultSubpaneControllerComponent with DefaultSubpaneViewController with ModelContainerComponent {
val model = self.model // Pass global dependency to the dynamic cake
}.asInstanceOf[Subpane]
}
Затем я добавляю DefaultSubpaneProvider
в торт верхнего уровня и добавляю SubpaneProvider
во все компоненты, которые должны создавать подпанели.
Проблема в этом подходе заключается в том, что мне приходится вручную передавать зависимости (model
в ModelContainerComponent
) от пирога верхнего уровня к динамически созданному пирогу. Это только тривиальный пример, но зависимостей может быть больше, а также может быть больше типов динамически создаваемых тортов. Все они требуют ручной передачи зависимостей; более того, простое изменение интерфейса какого-либо компонента может привести к большому количеству исправлений в нескольких провайдерах.
Есть ли более простой/чистый способ сделать это? Как эта проблема решается в шаблоне торта?
trait ModelContainerComponentProxy extends ModelContainerComponent { def originalModelContainer: ModelContainerComponentProxy; def model = originalModelContainer.model}
-- это могло бы решить как минимум проблему явной передачи всего содержимого компонентов. - person chemikadze   schedule 04.11.2014