티스토리 뷰

13. 컴포넌트 간의 의존 관계

  • 다수의 컴포넌트를 생성 및 상호 의존 관계를 맺을 수도 있음
  • 방법
    1. 서브 컴포넌트
    2. 컴포넌트 상속

서브 컴포넌트

  • 서브 컴포넌트 내에 다른 서브컴포넌트 구현
  • 두 컴포넌트를 연관 짓는 쉬운 방법
  • 서브 컴포넌트는 상위 컴포넌트에 바인딩된 모든 의존성을 제공 받음 (그 반대는 불가)

component

  • SubComponentB 는 SubComponentA, Component가 가진 모듈로 부터 의존성을 제공 받을 수 있음
  • SubComponentA 는 Component 모듈로 부터 의존성을 제공 받을 수 있음
  • Component 는 하위 컴포넌트 들이 가진 의존성을 제공 받을 수 없음 ❌

서브 컴포넌트의 정의

  • 추상 클래스 또는 인터페이스에 @Component 대신 @Subcomponent 어노테이션을 붙임
  • @Subcomponent 속성 modules에 바인딩 하려는 모듈 클래스들을 추가
  • 서브 컴포넌트를 만들려면 빌더 또는 팩토리를 반드시 정의 할것.

예제

  • 카페에가면.. 커피. 커피를 만들려면.. 에스프레소 머신(커피콩 + 물)
    • 카페 = 컴포넌트
    • 에스프레소 머신 = 서브 컴포넌트
import dagger.Subcomponent

@Subcomponent(modules = [MachineModule::class])
interface MachineComponent {
    fun getCoffee():Coffee
    fun inject(machine: Machine)

    @Subcomponent.Builder
    interface Builder{
        fun setMachineModule(coffeeMachineModule: MachineModule):Builder
        fun build():MachineComponent
    }
}
class Machine {
    private lateinit var component: MachineComponent

    constructor(builder: MachineComponent.Builder){ // 매개변수로 서브 컴포넌트로부터 빌더를 제공 받음
        component = builder.setMachineModule(MachineModule())
            .build()
        component.inject(this)
    }

    fun extract() = component.getCoffee()
}
import dagger.Module
import dagger.Provides

@Module
class MachineModule {
    @Provides
    fun provideCoffeeBean() = CoffeeBean()
}
import javax.inject.Inject

class Coffee @Inject constructor(coffeeBean: CoffeeBean, water: Water) {
}
class CoffeeBean {
}
class Water {
}

서브 컴포넌트 추가

  • 서브 컴포넌트(MachineComponent)를 정의 했고
  • 이제 컴포넌트에 서브 컴포넌트로 추가 해야함
  • 컴포넌트가 가진 @Module의 멤버인 subcomponent에 서브 컴포넌트 클래스를 추가
  • 서브 컴포넌트가 연결됬다면, 연결된 컴포넌트 모듈로부터 서브 컴포넌트의 빌더를 요청할 수 있음
import javax.inject.Inject

class Cafe {
    @Inject
    lateinit var coffeeMachine: Machine

    init{
        DaggerCafeComponent.create().inject(this)
    }

    fun orderCoffee() = coffeeMachine.extract()
}
import dagger.Component

@Component(modules = [CafeModule::class]) // CafeModule로부터 Machine을 제공 받음
interface CafeComponent {
    fun inject(cafe: Cafe)
}
import dagger.Module
import dagger.Provides

@Module(subcomponents = [MachineComponent::class])
class CafeModule {
    @Provides
    fun provideWater() = Water()

    @Provides
    fun provideMachine(builder: MachineComponent.Builder) = Machine(builder)
}

커피 주문 테스트 코드

반응형
class CoffeeUnitTest {
    @Test
    fun test_cafe(){
        val cafe = Cafe()
        println(cafe.orderCoffee())
        println(cafe.orderCoffee())
        println(cafe.orderCoffee())
    }
}
com.lilcode.hellodagger.subComponent.Coffee@72a9989
com.lilcode.hellodagger.subComponent.Coffee@4a0fd396
com.lilcode.hellodagger.subComponent.Coffee@32cd3972

서브 컴포넌트의 특징

cafe component

  • 머신-component 는 카페-component의 일부분
  • 머신-component는 카페-component 가 가진 카페-module 로부터 모듈이 가진 의존성을 제공 받을 수 있음
  • 하지만, 카페-component는 머신-component.builder를 제외하고는 서브 컴포넌트가 가진 모듈의 의존성을 제공 받을 수 없음.
  • 서브 컴포넌트는 독립적인 생명주기를 가짐
  • 컴포넌트가 존재하는 동안 서브 컴포넌트는 생성과 소멸을 반복할 수 있음.
  • 컴포넌트 소멸시 서브 컴포넌트도 같이 소멸

컴포넌트의 상속

  • 오브젝트 그래프 내의 하위 그래프를 작성하는 게 가장 간단하지만,
  • 서브 컴포넌트는 부모 컴포넌트와 밀접하여 분리가 어려움
  • 그 대안으로 컴포넌트의 상속이 있음
  • @Componentdependencies 속성에 상속 컴포넌트 클래스를 추가
  • 상속한 컴포넌트의 의존성 사용을 위해 상속한 컴포넌트는 해당 의존성을 프로비전 메서드로 제공해야함

그럼 예시를 구현해보자

[ComponentA]

import dagger.Component

@Component(modules = [ModuleA::class])
interface ComponentA {
    fun provideString(): String // 프로비전 메서드
}

[ModuleA]

import dagger.Module
import dagger.Provides

@Module
class ModuleA {
    @Provides
    fun provideString() = "String from ModuleA"
}

[ComponentB]

import dagger.Component

@Component(
    modules = [ModuleB::class],
    dependencies = [ComponentA::class]
)
interface ComponentB {
    fun inject(foo: Foo)
}

[ModuleB]

import dagger.Module
import dagger.Provides

@Module
class ModuleB {
    @Provides
    fun provideInteger() = 100
}

[Foo]

import javax.inject.Inject
import javax.inject.Named

class Foo {
    @Inject
    lateinit var str: String

    @set: [Inject Named("int")]
    var int: Int? = null
}

테스트

import org.junit.Test

class SubComponentInheritanceTest {
    @Test
    fun test_subComponentInheritance(){
        val foo = Foo()

        val componentA = DaggerComponentA.create()
        val componentB = DaggerComponentB.builder()
            .componentA(componentA)
            .build()
        componentB.inject(foo)

        println(foo.str) // String from ModuleA
        println(foo.int) // 100
    }
}
  • 컴포넌트B컴포넌트A 를 상속하여 컴포넌트A의 프로비전 메서드로 부터 String 타입 의존성을 제공 받을 수 있음

2021.07.27 - [Android/클린 아키텍처] - [Clean Architecture] 13-Dagger 멀티 바인딩 하기

2021.07.26 - [Android/클린 아키텍처] - [Clean Architecture] 12-바인딩의 종류 (Dagger)

2021.07.25 - [Android/클린 아키텍처] - [Clean Architecture] 11-범위 지정하기 (@Scope)

해당 글은 '아키텍처를 알아야 앱 개발이 보인다' 를 공부하며 요약 정리한 글 입니다.

댓글
최근에 올라온 글
최근에 달린 댓글
네이버 이웃추가
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함