Я попытался использовать шаблон торта в своем проекте и очень понравился, но есть одна проблема, которая меня беспокоит.
Рисунок пирога прост в использовании, когда все ваши компоненты имеют одинаковое время жизни. Вы просто определяете несколько компонентов-признаков, расширяете их посредством реализации признаков и затем объединяете эти реализации внутри одного объекта, а через само-типы все зависимости автоматически разрешаются.
Но предположим, что у вас есть компонент (со своими зависимостями), который может быть создан в результате действия пользователя. Этот компонент нельзя создать при запуске приложения, поскольку для него пока нет данных, но при его создании оно должно иметь автоматическое разрешение. Примером такого отношения компонентов является основное окно графического интерфейса пользователя и его сложные подэлементы (например, вкладка в панели ноутбука), которые создаются по запросу пользователя. Главное окно создается при запуске приложения, и некоторая подпапка в нем создается, когда пользователь выполняет какое-либо действие.
Это легко сделать в средах DI, таких как Guice: если я хочу, чтобы несколько экземпляров какого-то класса я просто вводил Provider<MyClass>
; затем я вызываю метод get()
этого провайдера, и все зависимости MyClass
автоматически разрешаются. Если MyClass
требует некоторых динамически вычисляемых данных, я могу использовать вспомогательное расширение для инъекций, но полученный код все еще сводится к провайдеру /factory. Связанная концепция, области, также помогает.
Но я не могу придумать хороший способ сделать это, используя шаблон торта. В настоящее время я использую что-то вроде этого:
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
in ModelContainerComponent
) вниз от торта верхнего уровня до динамически созданного торта. Это всего лишь тривиальный пример, но может быть больше зависимостей, а также может быть больше типов динамически созданных тортов. Все они требуют ручной передачи зависимостей; Более того, простое изменение интерфейса некоторых компонентов может привести к большому количеству исправлений в нескольких провайдерах.
Есть ли более простой/чистый способ сделать это? Как эта проблема разрешена в шаблоне торта?