Когда какой использовать?
Есть два способа сделать Subcomponent: связать его через ParentComponent или связать через ParentModule. Это показано в статье ниже
Сделать Subcomponent, связав через ParentComponent, тоже можно двумя способами.
1. Иметь функцию, которая обращается к самому ChildComponent
@Component interface ParentComponent { val childComponent: ChildComponent }
2. Иметь функцию, которая обращается к Builder ChildComponent.
@Component interface ParentComponent { val childComponentBuilder: ChildComponent.Builder }
Они очень похожи. Когда какой подход выбрать?
Сравнение
1. Стандартный код
- Доступ к самому ChildComponent имеет гораздо меньший объем шаблонного кода, так как ему не нужно кодировать
Builder
. Даже если нужно создать экземпляр модуля, это можно сделать, как показано ниже, без необходимости создавать для него сборщик ✅
@Component interface ParentComponent { val childComponent(module: ChildModule): ChildComponent }
- Получите доступ к конструктору дочерних компонентов, нужно сделать
Builder
на подкомпоненте, как показано ниже ❎
@Subcomponent(modules = [ChildModule::class]) interface ChildCompoenent { val requestHandler: RequestHandler @Subcomponent.Builder interface Builder { // The below module injection API is option if the // ChildModule doesn't take parameter. // But it is required if the Child module need to have // parameter fun childModule(module: ChildModule): Builder fun build(): RequestComponent } }
2. Генерация кода
Помимо шаблонного кода, давайте посмотрим на сгенерированный код.
- Доступ к самому ChildComponent генерирует меньшие коды. Сгенерированный код довольно прост ✅
// Generated codes @Override public ChildComponent getChildComponent() { return new ChildComponentImpl(); }
- Получите доступ к самому Builder ChildComponent, сгенерируйте дополнительный код Builder. Это добавит еще один слой кода ❎
// Generated codes @Override public ChildComponent.Builder getChildComponentBuilder() { return new ChildComponentBuilder(); } private final class ChildComponentBuilder implements ChildComponent.Builder { @Override public ChildComponent build() { return new ChildComponentImpl(new ChildModule()); } }
3. Родительский доступ к ChildComponent (обратная зависимость)
Как отмечалось в этом блоге, у нас может быть доступ Parent к ChildComponent и его объекту. (не только дочерний компонент, обращающийся к родительскому объекту)
- Доступ к ChildComponent не позволяет привязанному объекту ParentComponent напрямую обращаться к ChildComponent. Код ниже не работает ❎
@Singleton @Component interface ParentComponent { val childComponent: ChildComponent val myObject: MyObject } // This will fail with `cannot be provided` error @Singleton class MyObject @Inject constructor(childComponent: ChildComponent)
Это должно быть обходным путем с дополнительным обеспечением
@Singleton @Component interface ParentComponent { val childComponentBuilder: ChildComponent.Builder val myObject: MyObject } @Module class ParentModule { @Provides fun getChildComponent( childComponentBuilder: ChildComponent.Builder) = childComponentBuilder.build() } @Singleton class MyObject @Inject constructor(childCompoenent: ChildComponent)
- Получите доступ к Builder ChildComponent, разрешите привязанному объекту ParentComponent доступ к ChildComponent ✅
@Singleton @Component interface ParentComponent { val childComponentBuilder: ChildComponent.Builder val myObject: MyObject } @Singleton class MyObject @Inject constructor(childComponentBuilder: ChildComponent.Builder)
4. Привязка экземпляра к подкомпоненту
- Доступ к ChildComponent, мы не можем привязать экземпляр, который есть у ChildComponent ❎
- Получите доступ к Builder ChildComponent, можно легко привязать экземпляр к ChildComponent ✅
@Subcomponent(modules = [ChildModule::class]) interface ChildComponent { @Subcomponent.Builder interface Builder { @BindsInstance fun data(data: Data): Builder fun build(): ChildComponent } }
Как показано выше, мы можем привязать экземпляр data
к ChildModule через Builder.
5. Одна и та же копия против дубликатов
Как видно из сравнения 2, оба доступа к ChildComponent и его Builder также генерируют новую копию.
// Direct ChildComponent Access val parentComponent = DaggerParentComponent.create() parentComponent.childComponent // copy A parentComponent.childComponent // copy B (new one, not A) // Direct ChildComponent Builder Access val parentComponent = DaggerParentComponent.create() parentComponent.childComponentBuilder // copy A parentComponent.childComponentBuilder // copy B (new one, not A)
Таким образом, независимо от того, какой подход, всегда предоставляется новый объект. Следовательно, объект всегда дублируется.
Чтобы обеспечить создание того же объекта из ChildComponent, сохраните ChildComponent (либо тот, который получен непосредственно из ParentComponent, либо тот, который был создан из Builder ChildComponent), установите пользовательскую область для ChildComponent, а также установите пользовательскую область для объект, который вам нравится сохранять.
@ChildScope @Subcomponent(modules = [ChildModule::class]) interface ChildComponent { val childObject: ChildObject } @ChildScope class ChildObject @Inject constructor() @Scope @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) annotation class ChildScope
Ознакомьтесь с ниже для более подробной информации о Scope
TL;DR
Если требуются обратные зависимости или дополнительный экземпляр необходимо привязать к дочернему компоненту, используйте вместо этого построитель дочерних компонентов. В противном случае используйте подход прямого доступа к дочерним компонентам вместо доступа к его Builder.
Спасибо за чтение. Вы можете ознакомиться с другими моими темами здесь.
Подпишитесь на меня в medium, Twitter, Facebook или Reddit, чтобы получать небольшие советы и узнавать новое о разработке мобильных приложений и т. д. . ~Элье~