《 사칭 대역자 의존 주입 지침 》.

의존 주입은 안드로이드와 소프트웨어 개발에서 가장 핫한 화제 중의 하나이다.개발자들에게 많은 불안감과 사칭 증후군을 가져다 줄 화제다.
본고에서 우리는 DI가 왜 필요한지, 응용 프로그램에서 이를 어떻게 실현하는지 점차적으로 이해할 것이다.
관심 있는 경우 유튜브에서 다음 내용을 동영상으로 확인할 수 있습니다.
그것도 Android Essence에 발표되었다.

주입에 의존하는 코드가 없습니다
우선, 우리가 코드를 작성할 때 주입에 의존하지 않으면 발생할 수 있는 문제를 되돌아봅시다.다음 코드를 고려하십시오. 이 중 프로필ViewModel 클래스가 있습니다. 사용자가 프로필을 볼 때마다 이벤트를 추적하기를 원합니다.이런 방식을 통해 우리는 스크린이 다른 스크린에 비해 얼마나 흔한지 볼 수 있다.
class ProfileViewModel() {

    fun onProfileLoaded() {
        Firebase.analytics.logEvent("viewed_profile")
    }
}
언뜻 보기에 이 코드는 아무런 문제를 일으키지 않을 것 같다.사실, Firebase Analytics와 유사한 도구의 문서를 보면 이런 코드를 작성하는 것을 권장합니다.그러니 코드 라이브러리가 이렇게 보인다면 부끄러워하지 마세요.이러한 코드에 대해 나는 두 가지 구체적인 문제를 강조하고 싶다.
  • 저희 코드는Firebase Analytics에 심각하게 의존하기 때문에 다른 공급업체를 교체하려면 프로그램의 모든 ViewModel을 업데이트해야 합니다.
  • 우리는 이 코드를 테스트하기 어려울 것이다.

  • 테스트 곤란
    이 코드를 위한 좋은 단원 테스트는 언제 호출되는지 onProfileLoaded, 그리고 정확한 분석 이벤트를 추적했는지 검증할 수 있습니다.그러나 만약 우리가 이 테스트를 작성하기 시작한다면, 우리는 곧 문제를 발견할 것이다.Firebase에 누가 전화했는지 어떻게 확인할 수 있습니까?
    class ProfileViewModelTest {
    
        @Test
        fun verifyEventTracked() {
            val viewModel = ProfileViewModel()
            viewModel.onProfileLoaded()
    
            // No Ability To Verify Event Tracked
        }
    
    뿐만 아니라 이 특수한 테스트는 붕괴될 수도 있다.Firebase Analytics 라이브러리에서 진행 중인 일부 작업 때문에 주 루프가 시뮬레이션되지 않은 상태에서 런타임 예외가 발생합니다.
    Caused by: java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked.
    

    의존 주입
    저희 코드는Firebase 분석에 의존합니다.이 테스트 문제는 구조 함수에 삽입하여 해결할 수 있습니다.
    class ProfileViewModel(
        private val analytics: FirebaseAnalytics = Firebase.analytics,
    ) {
    
        fun onProfileLoaded() {
            analytics.logEvent("viewed_profile")
        }
    }
    
    이런 방법은 통상적으로'구조 함수 주입'이라고 불리며 의존항은 클래스의 구조 함수를 통해 제공한다.이것은 주입에 의존하는 핵심 개념이다.Kelly Shuster는 자신의 트윗에서 이 점을 잘 정리했다.

    켈리 슈스터🇺🇲 💙
    # 켈리슈스트

    네, 질문이 하나 있습니다. 왜 그것은 정보를 전달하는 것이 아니라 의존 주입이라고 불리나요?🤔
    2018년 7월 20일 오후 17:43
    DI는 하나의 개념에 대해 복잡한 이름을 가지고 있어'전달 내용'으로 간소화할 수 있다

    테스트 업데이트
    이제 Firebase Analytics의 가짜 구현을 제공하기 위해 테스트를 업데이트할 수 있는 의존적인 방법이 생겼습니다. 그리고 정확한 이벤트를 추적했는지 확인하기 위해 사용할 수 있습니다.
    class ProfileViewModelTest {
    
        @Test
        fun verifyEventTracked() {
            val mockAnalytics = FakeFirebaseAnalytics()
            val viewModel = ProfileViewModel(mockAnalytics)
            viewModel.onProfileLoaded()
    
            mockAnalytics.verifyEventLogged("viewed_profile")
        }
    }
    
    그러나 우리가 이런 일을 할 때, 우리는 앞에서 토론한 또 다른 문제를 고려해야 한다.이러한 의존성을 주입했음에도 불구하고Firebase 분석에 강하게 의존한다.우리는 이런 상황을 피해야만 미래에 서로 다른 분석 서비스를 제공하는 능력을 지원할 수 있다.

    포장 의존 항목
    AnalyticsTracker와 Firebase 구현을 정의하는 인터페이스를 만들 수 있습니다.장래에 우리는 다른 서비스를 위해 다른 실현을 창조할 수 있다.
    interface AnalyticsTracker {
        fun trackEvent(eventName: String)
    }
    
    class FirebaseAnalyticsTracker : AnalyticsTracker {
        override fun trackEvent(eventName: String) {
            Firebase.analytics.logEvent(eventName)
        }
    }
    
    이제 ViewModel을 이 인터페이스에 종속시킬 수 있습니다.
    class ProfileViewModel(
        private val analytics: AnalyticsTracker = FirebaseAnalyticsTracker(),
    )
    
    이 블로그에서 당신은 주입에 의존하는 핵심 개념, 우리가 왜 그것을 필요로 하는지, 그리고 주어진 유형을 위해 그것을 어떻게 실현하는지 이해했다.그럼에도 불구하고 우리는 새로운 문제에 대답할 것이다.

    Why does dependency injection seem so much more complicated than passing stuff in?


    의존항 주입에 대한 몇 가지 고급 테마가 있습니다. 공유 의존항부터 시작합니다.우리는 우리의 분석 추적 예시를 계속할 수 있다.분석이 없는 곳이 없기 때문에, 우리는 모든 화면에 분석 추적기의 실례를 제공할 수 있기를 희망한다.모든 화면이 업데이트될 수 있도록 설정을 만들고 싶습니다.

    의존 주입 설계도
    이 목표를 실현하기 위해서 우리는 세 가지 절차를 따를 수 있다.
  • 프로그램에서 사용하는 의존 항목을 저장할 용기를 만듭니다.
  • 클래스를 의존항의 숙주로 수정한다.
  • 이러한 의존 항목을 요청하기 위해 단일 화면을 업데이트합니다.
  • 첫 번째 단계를 실현하기 위해서 우리는 Application라는 클래스를 만들 수 있다.Google analytics tracker가 초기에 인터페이스에 어떻게 입력되었는지 주의하십시오.Firebase 구현으로 되돌아갑니다.
    class AppDependencies {
        val analyticsTracker: AnalyticsTracker
            get() = FirebaseAnalyticsTracker()
    }
    
    다음에 우리는 AppDependencies 클래스에서 이 의존항 용기에 대한 인용을 만들 수 있다.
    class MyApp : Application() {
        val appDependencies = AppDependencies()
    }
    
    마지막으로, 활동 또는 세션에서 응용 프로그램 컨텍스트에 대한 참조를 가져와 종속성을 요청할 수 있습니다.
    class ProfileActivity : Activity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            val appDependencies = (this.application as MyApp).appDependencies
            val analyticsTracker = appDependencies.analyticsTracker
            val profileViewModel = ProfileViewModel(analyticsTracker)
        }
    }
    
    this Android guide에서 수동 의존항 주입에 대한 더 많은 정보를 알 수 있습니다.

    주입 라이브러리 의존
    몇 달마다 안드로이드 트위터에는 주입고에 의존하는 플래머워어를 사용해야 하는지에 대한 이야기가 올라온다.이것은 사람들이 의존 주입에 대해 토론할 때 직면하는 곤혹과 초조함을 격화시킬 뿐이다. 이것이 바로 토론의 의존 주입이 본문 말미에 보류된 이유이다.디 라이브러리의 존재는 수동적인 방법의 템플릿 코드를 줄이는 데 도움이 되지만, 실제로는 같은 방법을 따른다. 의존항을 만들어서 응용 프로그램 클래스에 저장하고, 이러한 의존항을 요청하는 메커니즘을 만든다.
    두 개의 의존항이 입고되었는데, 그것이 어떻게 실현되었는지 간략하게 소개합니다.

    칼자루
    Hilt는 구글이 공식적으로 추천한 의존 주입고다.그것은 주석 처리를 통해 의존 관계를 관리한다.더 많은 정보를 얻기 위해 공식 문서를 볼 것을 권장하지만, 힐트가 DI 레시피를 어떻게 사용했는지 되돌아봅시다.
    우선, 우리는 Application in-Hilt를 만들어서 의존항을 정의할 수 있습니다.다음은 모든 주석을 두려워하지 말라는 예시 모듈이다.여기서 중요한 점은 우리가 Module 유형의 AnalyticsTracker 실례를 만들 수 있는 방법이 있다는 것이다.
    @Module
    @InstallIn(ActivityComponent::class)
    abstract class AnalyticsModule {
    
      @Binds
      abstract fun bindAnalyticsTracker(
        analyticsTracker: FirebaseAnalyticsTracker
      ): AnalyticsTracker
    }
    
    다음에 우리는 응용 프로그램 종류를 설정할 수 있다.이 작업은 간단해졌습니다. FirebaseAnalyticsTracker 주석을 사용하기만 하면 됩니다.
    @HiltAndroidApp
    class MyApp : Application()
    
    마지막으로, 우리는 의존항을 요청하는 메커니즘이 필요하다.칼자루에 대해서는 @HiltAndroidApp 주석만 사용합니다.
    @AndroidEntryPoint
    class ProfileActivity : Activity() {
    
        @Inject 
        lateinit var analytics: AnalyticsTracker
    }
    

    코인
    또 다른 일반적인 DI 라이브러리는 Koin입니다.문법과 기술의 실현은 다르지만 우리는 같은 방법을 사용하는 것을 다시 한 번 보았다.
    종속성 컬렉션을 만들려면 다음과 같이 하십시오.
    val analyticsModule = module {
        single<AnalyticsTracker> {
            FirebaseAnalyticsTracker()
        }
    }
    
    애플리케이션 클래스에 저장:
    class MyApp : Application() {
        override fun onCreate() {
            super.onCreate()
    
            startKoin {
                modules(analyticsModule)
            }
        }
    }
    
    당신의 활동에서 그들에게 다음과 같이 요구합니다.
    class ProfileActivity : Activity() {
    
        val analytics: AnalyticsTracker by inject()
    }
    
    오늘 우리는 힐트와 코인 사이의 미세한 차이를 깊이 연구하지 않을 것이다.반대로, 나는 단지 이 라이브러리에 대한 개술을 제공하고, 그것들이 우리 자신의 수동 의존 주입을 작성하는 것과 같은 절차를 따르는 것을 설명하고 싶을 뿐이다.

    요점을 요약하고 다시 말하다.
    사용자가 선택한 의존항 주입은 우리가 왜 의존항 주입을 먼저 필요로 하는지보다 훨씬 중요합니다.이 글은 의존항 주입이 어떻게 우리의 코드 라이브러리에서 더 좋은 테스트를 실현할 수 있는지, 그리고 장래에 어떤 이유로든 의존항을 교환할 수 있는 능력을 되돌아보고 모든 응용 프로그램 의존항을 집중적으로 관리할 수 있는 위치를 제공했다.
    이것은 당신이 이 복잡한 화제에 대한 초조함을 완화시키는 데 도움이 됩니까?댓글로 우리가 앞으로 주목해야 할 다른 사칭증후군을 유발하는 이슈를 알려주세요.

    좋은 웹페이지 즐겨찾기