依赖于具有不同范围的其他组件的组件(具有不同范围的组件层次结构)

时间:2017-05-24 12:01:22

标签: dependency-injection composition dagger-2

我的Android项目中有复杂的多层架构。

目前我想使用DI组件和模块的以下结构:

[Data Layer]
    @DataScope //scope is used for caching (Singleton) some Data Layer entities for whole application
    - DataComponent //exposes just interfaces which should be used on the BL Layer
        //Modules exposes entities for internal (Data Layer) injections and entities which exposed by DataComponent for BL Layer
        * DataModule1
        * DataModule2
        * DataModule3

[Business Logic Layer] (also has component dependency on DataComponent)
    @BlScope //scope is used for caching (Singleton) some BL Layer entities for whole application
    - BlComponent //exposes just interfaces which should be used on the Service Layer & Presentation Layer
        //Modules exposes entities for internal (BL Layer) injections and entities which exposed by BLComponent for the Service Layer & Presentation Layer
        * BlModule1
        * BlModule2

[Service Layer] (also has component dependency on BlComponent) - this layer has Android specific entities (Android Service, ContentProvider) not related to the Presentation Layer
    @ServiceScope //scope is used for caching (Singleton) some Service Layer entities for whole application
    - ServiceComponent //exposes just interfaces which should be used on the Presentation Layer
        * ServiceModule //Module exposes entities for internal (Service Layer) injections and entities which exposed by ServiceComponent for the Presentation Layer

[Presentation Layer] (also has component dependency on: ServiceComponent, BlComponent)
    @PresentationScope //scope is used for caching (Singleton) some Presentation Layer entities for whole application
    - PresentationComponent //exposes just interfaces which should be used on the current layer
        * PresentationModule //Module exposes entities injections on the current layer

服务组件& BlComponent不公开类似的接口。

要构建主图,请使用以下代码:

DataComponent dataComponent = DaggerDataComponent.builder().<addModules>.build();
BlComponent dataComponent = DaggerBlComponent.builder().<addModules>.dataComponent(dataComponent).build();
ServiceComponent serviceComponent = DaggerServiceComponent.builder().<addModule>.blComponent(blComponent).build();
PresentationComponent presentationComponent = DaggerPresentationComponent.builder().<addModule>.blComponent(blComponent).serviceComponent(serviceComponent).build();

在PresentationLayer中,我只使用&#34; presentationComponent&#34;从ServiceComponent / Layer和BLComponent / Layer提供所需的依赖项。

目前上面的情况不起作用,因为PresentationComponent依赖于带有错误的2个范围的组件

  

&#34; ...取决于多个范围内的组件:...&#34;

虽然它允许使用具有许多非范围组件的一个范围组件。该体系结构旨在限制在上层使用内部层实体,同时在每个层/模块上进行独立测试(单元和仪器)。

有人能帮助我理解Dagger处理器的错误或理想行为吗? (为什么?) 关于Dagger2回购的问题:https://github.com/google/dagger/issues/747#issuecomment-303526785

1 个答案:

答案 0 :(得分:3)

这不是错误,它是一个功能

以您建议的方式允许混合范围所产生的问题类似于the problems of multiple inheritance in OO languages

考虑以下组件结构(钻石问题):

    A
   / \
  B   C
   \ /
    D
{p} DB

C子组件。由于兄弟姐妹BC彼此独立,因此他们可以自由地向子组件公开任意依赖Foo。假设组件Foo中的模块集需要DFoo D现在应该使用哪个B?来自C的那个或来自@Singleton的那个?

如果允许范围,情况会更复杂。 Dagger 2中的范围用于标记生命周期。换句话说,它说&#34;我将在这一点保持对这些绑定的引用&#34;。因此,您的@AppScoped范围绑定必须分组在一个Component中,您可以在Application子类的级别维护一个引用。例如,它不会将B组件初始化为方法中的局部变量或作为活动的字段 - 它将随方法堆栈一起被丢弃或在销毁活动时被销毁。

回到上面的例子,假设CFoo具有不同的范围。 B中绑定的@BScopedB。所以它应该只活D。但@CScoped Foo可以从C获取B。因此,我们无法确定@BScoped Foo是否正在向其子组件公开Foo - 我们已允许GSON逃避其范围。这是由于不限制我们自己的有向无环图所呈现的处理问题。正如您可能已经意识到的那样,一旦我们不允许循环,计算机科学中的许多问题就会变得更容易解决。

只有在添加范围注释时才会收到设置的错误消息,并且在删除它时它会消失,这并不表示Dagger 2中存在错误或缺少某些内容。它只是意味着没有范围注释,您只有组件层次结构的错觉,因为所有组件实际上是相等的。同样,即使您认为通过非范围和范围组件的组合以某种方式制作钻石,它也不会真正成为钻石,因为非范围组件中没有真正的层次结构。

您提出的架构是出于好意,因为您正试图实现的分离。但是,Dagger 2组件用于分组生命周期,而不是分层。为了说明这一点,大多数Android应用程序都具有应用程序范围的依赖关系,例如SharedPreferences@Singleton等。这些在应用程序的整个生命周期中一起生存和死亡的依赖关系通常都绑定在为@PerApp组件设置的模块。您也可以将其称为@AppScopedApplication - 只要您保留对@UserScope子类中的组件的引用,它就无关紧要。

directed acyclic graph(DAG格中的DAG)中,你可以放置你想要的东西。只要应用有登录用户,有些人就会生成@PerActivity。其他人跳过此步骤直接转到@ActivityScoped@PerActivity组件。 {{1}}组件的模块集应绑定具有单个活动生命周期的成员,例如活动的上下文。

此时,最好按照Google Android Architecture Blueprint中的功能进行分组。虽然此级别的组件将注入来自不同层(模型层和表示层)的依赖关系,但这些组件可以由judicious use of modules分隔。此外,您可以将成员分组在同一个Java包中,这意味着您可以使用访问修饰符来实现有效的Java项13:最小化类和成员的可访问性。