ส่วนประกอบย่อยของ Dagger 2 เทียบกับการพึ่งพาส่วนประกอบ


138

plus()วิธีการของ Dagger 1 เป็นสิ่งที่ฉันใช้บ่อยในแอปพลิเคชันก่อนหน้านี้ดังนั้นฉันจึงเข้าใจสถานการณ์ที่คุณอาจต้องการมีส่วนประกอบย่อยที่สามารถเข้าถึงการเชื่อมโยงกราฟพาเรนต์ได้อย่างเต็มที่

การใช้การพึ่งพาองค์ประกอบแทนการพึ่งพาองค์ประกอบย่อยจะเป็นประโยชน์ในสถานการณ์ใดและเพราะเหตุใด

คำตอบ:


234

การพึ่งพาส่วนประกอบ - ใช้สิ่งนี้เมื่อคุณต้องการให้สององค์ประกอบเป็นอิสระ

ส่วนประกอบย่อย - ใช้สิ่งนี้เมื่อคุณต้องการให้สององค์ประกอบคู่กัน


ฉันจะใช้ด้านล่างตัวอย่างเพื่ออธิบายการพึ่งพาชิ้นส่วนและส่วนประกอบย่อย บางจุดที่ควรสังเกตเกี่ยวกับตัวอย่าง ได้แก่ :

  • SomeClassA1สามารถสร้างได้โดยไม่ต้องพึ่งพาใด ๆ ModuleAให้และตัวอย่างของSomeClassA1ผ่านทางprovideSomeClassA1()วิธีการ
  • SomeClassB1SomeClassA1ไม่สามารถสร้างได้โดยไม่ต้อง ModuleBสามารถระบุอินสแตนซ์ได้SomeClassB1ก็ต่อเมื่ออินสแตนซ์SomeClassA1ถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังprovideSomeClassB1()เมธอด
@Module
public class ModuleA {
    @Provides
    public SomeClassA1 provideSomeClassA1() {
        return new SomeClassA1();
    }
}

@Module
public class ModuleB {
    @Provides
    public SomeClassB1 provideSomeClassB1(SomeClassA1 someClassA1) {
        return new SomeClassB1(someClassA1);
    }
}

public class SomeClassA1 {
    public SomeClassA1() {}
}

public class SomeClassB1 {
    private SomeClassA1 someClassA1;

    public SomeClassB1(SomeClassA1 someClassA1) {
        this.someClassA1 = someClassA1;
    }
}

Dagger จะดูแลการส่งผ่านอินสแตนซ์ของSomeClassA1เป็นอาร์กิวเมนต์ไปยังprovideSomeClassB1()วิธีการModuleBเมื่อใดก็ตามที่ModuleBมีการเตรียมใช้งานการประกาศ Component / Subcomponent เราจำเป็นต้องสั่งให้ Dagger รู้วิธีเติมเต็มการพึ่งพา ซึ่งสามารถทำได้โดยการใช้การพึ่งพาชิ้นส่วนหรือองค์ประกอบย่อย

การพึ่งพาส่วนประกอบ

สังเกตประเด็นต่อไปนี้ในตัวอย่างการอ้างอิงส่วนประกอบด้านล่าง:

  • ComponentBต้องกำหนดการพึ่งพาผ่านdependenciesวิธีการในการใส่@Componentคำอธิบายประกอบ
  • ComponentAModuleBไม่จำเป็นต้องประกาศ สิ่งนี้ทำให้ทั้งสององค์ประกอบเป็นอิสระ
public class ComponentDependency {
    @Component(modules = ModuleA.class)
    public interface ComponentA {
        SomeClassA1 someClassA1();
    }

    @Component(modules = ModuleB.class, dependencies = ComponentA.class)
    public interface ComponentB {
        SomeClassB1 someClassB1();
    }

    public static void main(String[] args) {
        ModuleA moduleA = new ModuleA();
        ComponentA componentA = DaggerComponentDependency_ComponentA.builder()
                .moduleA(moduleA)
                .build();

        ModuleB moduleB = new ModuleB();
        ComponentB componentB = DaggerComponentDependency_ComponentB.builder()
                .moduleB(moduleB)
                .componentA(componentA)
                .build();
    }
}

SubComponent

สังเกตประเด็นต่อไปนี้ในตัวอย่าง SubComponent:

  • เนื่องจากComponentBไม่ได้กำหนดการพึ่งพาModuleAจึงไม่สามารถอยู่อย่างอิสระได้ จะขึ้นอยู่กับส่วนประกอบที่จะให้ไฟล์ModuleA. ดังนั้นจึงมี@Subcomponentคำอธิบายประกอบ
  • ComponentAได้ประกาศModuleBผ่านวิธีการเชื่อมcomponentB()ต่อ สิ่งนี้ทำให้ทั้งสององค์ประกอบคู่กัน ในความเป็นจริงComponentBสามารถเริ่มต้นผ่านทางComponentA.
public class SubComponent {
    @Component(modules = ModuleA.class)
    public interface ComponentA {
        ComponentB componentB(ModuleB moduleB);
    }

    @Subcomponent(modules = ModuleB.class)
    public interface ComponentB {
        SomeClassB1 someClassB1();
    }

    public static void main(String[] args) {
        ModuleA moduleA = new ModuleA();
        ComponentA componentA = DaggerSubComponent_ComponentA.builder()
                .moduleA(moduleA)
                .build();

        ModuleB moduleB = new ModuleB();
        ComponentB componentB = componentA.componentB(moduleB);
    }
}

5
ฉันมีการตั้งค่าส่วนประกอบย่อยที่ไม่ได้เพิ่ม Module B ไปยัง ComponentA ซึ่งหมายความว่า componentA builder ไม่ต้องการ moduleB ดูเหมือนว่าจะได้ผลในแบบที่ฉันคาดไว้ว่าจะอนุญาตให้สร้าง ComponentA เมื่อเริ่มแอปพลิเคชันแล้วสร้างอินสแตนซ์ m
FriendlyMikhail

2
@MikeN - คุณสามารถเน้นว่าคุณสามารถกำจัด ModuleB บน ComponentA ได้อย่างไร? ฉันสามารถกำจัด ModuleB บน ComponentA ได้ก็ต่อเมื่อฉันให้ขอบเขตที่แตกต่างกันบน ComponentA และ ComponentB
Praveer Gupta

1
คุณถูกต้องการตั้งค่าของฉันใช้งานได้เพราะอยู่คนละขอบเขตกัน ขอโทษ.
FriendlyMikhail

2
" SomeClassB1ขึ้นอยู่กับSomeClassA1. ComponentAต้องกำหนดการพึ่งพาอย่างชัดเจน" ==> คุณหมายถึง " ComponentBต้องกำหนดการอ้างอิงอย่างชัดเจน" หรือไม่?
Tar

1
เช่นเดียวกับสิ่งที่ @Tar ชี้ฉันเข้าใจว่าใน " SomeClassB1ขึ้นอยู่กับSomeClassA1. ComponentAไม่จำเป็นต้องกำหนดการอ้างอิงอย่างชัดเจน" คุณหมายถึง " ComponentBไม่จำเป็นต้องกำหนดการอ้างอิงอย่างชัดเจน"
Sebas LG

45

ตามเอกสาร :

Component Dependencyช่วยให้คุณเข้าถึงเฉพาะการเชื่อมโยงที่เปิดเผยเป็นวิธีการจัดเตรียมผ่านการอ้างอิงส่วนประกอบกล่าวคือคุณสามารถเข้าถึงได้เฉพาะประเภทที่ประกาศในพาเรนComponentต์

SubComponentช่วยให้คุณสามารถเข้าถึงกราฟการผูกทั้งหมดจากพาเรนต์เมื่อมีการประกาศกล่าวคือคุณสามารถเข้าถึงอ็อบเจ็กต์ทั้งหมดที่ประกาศในModules

สมมติว่าคุณมีApplicationComponentมีทุกAndroidสิ่งที่เกี่ยวข้องกัน ( LocationService, Resources, SharedPreferenceฯลฯ ) คุณยังต้องการให้DataComponentคุณจัดการสิ่งต่างๆเพื่อความคงอยู่ควบคู่ไปกับWebServiceการจัดการกับ API สิ่งเดียวที่คุณขาดDataComponentเป็นที่อาศัยอยู่ในApplication Context ApplicationComponentวิธีที่ง่ายที่สุดที่จะได้รับContextจากจะเป็นการพึ่งพาDataComponent ApplicationComponentคุณต้องแน่ใจว่าคุณได้รับการContextประกาศอย่างชัดเจนApplicationComponentเนื่องจากคุณมีสิทธิ์เข้าถึงเนื้อหาที่ประกาศเท่านั้น ในกรณีนี้ไม่มีการทำงานด้วยตนเองหมายความว่าคุณไม่จำเป็นต้องระบุSubmodulesในพาเรนต์Componentและเพิ่มโมดูลย่อยของคุณลงในโมดูลพาเรนต์อย่างชัดเจนเช่น:

MySubcomponent mySubcomponent = myComponent.plus(new ChildGraphModule("child!")); // No need!

ตอนนี้พิจารณากรณีที่ว่าที่คุณต้องการที่จะฉีดWebServiceจากDataComponentและLocationServiceจากการApplicationComponentเข้ามาของคุณFragmentซึ่งผูกใช้@Submodule plusคุณสมบัติดังกล่าวข้างต้น สิ่งที่น่าสนใจก็คือองค์ประกอบที่คุณเชื่อมโยงกับ ( ApplicationComponent) ไม่จำเป็นต้องเปิดเผยWebServiceหรือLocationServiceเนื่องจากคุณสามารถเข้าถึงกราฟทั้งหมดได้ทันที


2
@Submoduleถ้าผมเข้าใจอย่างถูกต้องมีอินเตอร์เฟซที่เรียกว่าไม่มี พิมพ์ผิดหรือเปล่า
Islam Salah

ฉันชอบวิธีที่ใช้ตัวอย่างชีวิตจริงเพื่อแสดงความแตกต่าง อย่างไรก็ตามสิ่งนี้สับสนกว่าการอ่านเอกสาร จะช่วยให้มีclassesตัวอย่างน้อยลงและมีรูปภาพมากขึ้นเพื่อแสดงจุดที่แน่นอน
sudocoder

18

นี่คือตัวอย่างโค้ดพร้อมสกรีนช็อตเพื่อความเข้าใจส่วนประกอบและส่วนประกอบย่อยมากขึ้น:

ส่วนประกอบ: ป้อนคำอธิบายภาพที่นี่

  1. AppComponent มีการประกาศสองรายการ
  2. AppComponent เริ่มต้นในคลาส App
  3. HomeActivityComponent ขึ้นอยู่กับ AppComponent
  4. ใน HomeActivity เกี่ยวกับการเริ่มต้น DaggerHomeActivityComponent ฉันกำลังให้วัตถุ AppComponent เป็นองค์ประกอบ

SubComponent:

ป้อนคำอธิบายภาพที่นี่

  1. AppComponent ประกอบด้วย SubComponent หรือ SubComponents
  2. AppComponent เริ่มต้นในคลาส App
  3. SubComponent ไม่รู้เกี่ยวกับ ParentComponent ของเขา ที่ให้การอ้างอิงของตัวเองโดยรวมโมดูลเท่านั้น
  4. ใน HomeActivity ฉันกำลังฉีด SubComponent โดยใช้ Parent Component

และแผนภาพภาพ: ป้อนคำอธิบายภาพที่นี่

ที่มา: ลิงค์


แผนภาพจะไม่สมเหตุสมผลกว่านี้หรือไม่หากส่วนประกอบย่อยแนบ AppComponent?
Florian Walther

1

อีกสิ่งหนึ่งที่ฉันไม่เคยรู้มาก่อนก็คือ:

  • @Subcomponentเช่นมีตรงส่วนประกอบครอบครัวหนึ่ง (แม้ว่าส่วนประกอบที่แตกต่างกันสามารถยกตัวอย่างที่เดียวกัน@Subcomponentและเป็นผู้ปกครองเช่นนั้น)
  • A @Componentอาจมีคอมโพเนนต์ "พาเรนต์" เป็นศูนย์หนึ่งหรือหลายรายการที่ประกาศผ่านการอ้างอิงส่วนประกอบ

1
ในกรณีที่สองการพูดว่า '@Component' อาจไม่ถูกต้องอาจมี ... parent (s) '@Component' ค่อนข้างไม่มีผู้ปกครอง แต่อย่างอื่นอาจขึ้นอยู่กับสิ่งนั้น (เพียงแค่ใช้) ผ่านการอ้างอิงส่วนประกอบ
demaksee

@demaksee ฉันไม่รู้สำหรับฉันแล้วดูเหมือนว่าถ้าคุณแมปลำดับชั้นส่วนประกอบของคุณคุณจะได้รับที่ DAG และฉันคิดว่ามันเป็นวิธีมาตรฐานในการอ้างถึงความสัมพันธ์นี้ในฐานะผู้ปกครองและเด็กในบริบทกราฟ หากเรากำลังพูดถึงผลงานภายในของ Dagger ฉันเดาว่ามันอาจจะไม่ใช่คำที่ถูกต้อง
arekolek
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.