티스토리 뷰

12. 멀티 바인딩 하기

  • dagger의 멀티 바인딩을 사용하면 여러 모듈에 있는 같은 타입의 객체를 하나의 set, map 형태로 관리 가능

Set 멀티 바인딩

  • @IntoSet, @ElementsIntoSet -> @Provides

예제 코드

import dagger.Module
import dagger.Provides
import dagger.multibindings.ElementsIntoSet
import dagger.multibindings.IntoSet

@Module
class SetModule {
    @Provides
    @IntoSet
    fun provideHello() = "Hello"

    @Provides
    @IntoSet
    fun provideWorld() = "World"

    @Provides
    @ElementsIntoSet
    fun provideSet() = setOf<String>("Lee", "Kim")// set 일부를 한번에 추가
}
import dagger.Component

@Component(modules = [SetModule::class])
interface SetComponent {
    fun inject(foo: Foo)
}
import javax.inject.Inject

class Foo {
    @Inject
    lateinit var strings: Set<String>

    fun print() {
        for (item in strings) {
            println(item)
        }
    }
}

테스트 코드

    @Test
    fun test_multiBinding(){
        val foo = Foo()
        DaggerSetComponent.create().inject(foo)
        foo.print()
    }

출력

Hello
World
Lee
Kim    

Map 멀티 바인딩

  • 모듈 내부에 @Provides 메서드에 @IntoMap 추가
  • 주의사항 : Map 사용 시 Key 필요하므로. 별도 키 어노테이션이 필요함

기본 제공 키 종류

  • @StringKey
  • @ClassKey
  • @IntKey
  • @LongKey

예시 코드

class Foo {
}

import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey

@Module
class MapModule {
    companion object {
        @Provides
        @IntoMap
        @StringKey("foo")
        fun provideFooValue() = 100L

        @Provides
        @IntoMap
        @ClassKey(Foo::class)
        fun provideFooStr() = "Foo String"
    }
}
import dagger.Component

@Component(modules = [MapModule::class])
interface MapComponent {
    fun getLongByString(): Map<String, Long>
    fun getStringByClass(): Map<Class<*>, String>
}

테스트 코드

    @Test
    fun test_multiBindingMap(){
        val component = DaggerMapComponent.create()
        val value = component.getLongByString()["foo"]
        val str = component.getStringByClass()[Foo::class.java]

        println(value)
        println(str)
    }
100
Foo String

사용자 정의 키 만들기

  • @MapKey 어노테이션 사용

AnimalKey, NumberKey를 사용하여 멀티 바인드 하는 예제

enum class Animal {
    CAT, DOG
}
import dagger.MapKey

@MapKey
annotation class AnimalKey(val value: Animal)
import dagger.MapKey
import kotlin.reflect.KClass


@MapKey
annotation class NumberKey(val value: KClass<out Number>) // Number 클래스의 서브 클래스 타입을 속성으로.
import dagger.Component

@Component(modules = [MapModule::class])
interface MapKeyComponent {
    fun getStringsByAnimal(): Map<Animal, String>
    fun getStringsByNumber(): Map<Class<out Number>, String>
}
반응형
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoMap

@Module
class MapModule {
    @IntoMap
    @AnimalKey(Animal.CAT)
    @Provides
    fun provideCat() = "Meow"

    @IntoMap
    @AnimalKey(Animal.DOG)
    @Provides
    fun provideDog() = "Bow-wow"

    @IntoMap
    @NumberKey(Float::class)
    @Provides
    fun provideFloatValue() = "100f"

    @IntoMap
    @NumberKey(Int::class)
    @Provides
    fun provideIntValue() = "1"
}

테스트 코드

    @Test
    fun test_customMapKey(){
        val component = DaggerMapKeyComponent.create()
        val cat = component.getStringsByAnimal()[Animal.CAT]
        val dog = component.getStringsByAnimal()[Animal.DOG]
        val number = component.getStringsByNumber()[Float::class.java]

        println(cat)
        println(dog)
        println(number)
    }

출력

Meow
Bow-wow
100f

상속된 서브 컴포넌트의 멀티 바인딩

  • 바인드된 set, map을 서브 컴포넌트도 그대로 물려 받을 수 있음

예제 코드

import dagger.Component

@Component(modules = [ParentModule::class])
interface ParentComponent {
    fun strings():Set<String>
    fun childCompBuilder(): ChildComponent.Builder
}
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet

@Module(subcomponents = [ChildComponent::class])
class ParentModule {
    @Provides
    @IntoSet
    fun string1() = "parent string 1"

    @Provides
    @IntoSet
    fun string2() = "parent string 2"
}
import dagger.Subcomponent

@Subcomponent(modules = [ChildModule::class])
interface ChildComponent {
    fun strings(): Set<String>

    @Subcomponent.Builder
    interface Builder{
        fun build():ChildComponent
    }
}
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet

@Module
class ChildModule {
    @Provides
    @IntoSet
    fun string3() = "child string 1"

    @Provides @IntoSet
    fun string4() = "child string 2"
}

테스트 코드


    @Test
    fun test_multiBindingSubcomponent(){
        val parentComp = DaggerParentComponent.create()
        val childComp = parentComp.childCompBuilder().build()

        println("List set in Parent")
        var itr = parentComp.strings().iterator()
        while(itr.hasNext()){
            println(itr.next())
        }

        println("List set in Child")

        itr = childComp.strings().iterator()
        while(itr.hasNext()){
            println(itr.next())
        }
    }

출력

    List set in Parent
    parent string 1
    parent string 2
    List set in Child
    child string 2
    child string 1
    parent string 1
    parent string 2

추상적인 멀티 바인딩 선언하기

  • 컴포넌트는 여러 모듈을 사용할 수 있는데 다른 모듈에 의해서 멀티 바인드를 사용할 수도 있고 안할 수도 있다.
  • 멀티 바인딩 사용 여부와 상관 없이 @Multibinds 어노테이션으로 멀티 바인딩 선언 가능
  • 매개 변수를 갖지 않는 추상 메서드에 사용 가능, 반환 타입은 Map 이나 Set
import dagger.Component

@Component(modules = [MultibindsModules::class])
interface MultibindsComponent {
    fun getStrings(): Set<String>
}

import dagger.Module
import dagger.multibindings.Multibinds

@Module
abstract class MultibindsModules {
    @Multibinds
    abstract fun strings(): Set<String>
}

테스트

    @Test
    fun test_multiBinds(){
        val component = DaggerMultibindsComponent.create()

        // 비어 있음
        for(s in component.getStrings()){
            println(s)
        }
    }
  • 비어있기 때문에 아무 출력 없음
  • 비어 있는 Set을 멀티 바인딩한 효과와 같아 @ElementsIntoSet 만 사용해서 멀티 바인딩일 선언할 수도 있음
@Module
abstract class MultibindsModules {
    companion object{
        @Provides
        @ElementsIntoSet
        fun emptyStrings() = Collections.emptySet<String>()
    }
}

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

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

2021.07.24 - [Android/클린 아키텍처] - [Clean Architecture] 10-한정자 (@named)

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

댓글
최근에 올라온 글
최근에 달린 댓글
네이버 이웃추가
«   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
글 보관함