본문 바로가기

안드로이드

Dependncy injection

참조한 글은 안드로이드 공식문서 입니다.

의존성 주입을 하면 아래와 같은 이점이 존재한다.
  • 코드 재활용
  • 리팩토링하기 편함
  • 테스팅 하기 편함
안드로이드에서 의존성 주입을 하기 이전에 이 섹션에서는 근본적으로 의존성 주입이 뭔지 개략적으로 설명해준다. (고마워라.. 근데 다른 문서에서도 이렇게 해주면 안되냐?)
의존성 주입이란?
위의 이점에서도 언급했듯이 리팩토링을 해야하거나 코드를 재활용하기 좋게 만드는 것을 말한다.안드로이드에서 의존성 주입은 두가지가 존재한다.
 
  1. 생성자에 의한 의존성 주입
  2. 멤버변수에 의한 의존성 주입
 
하나씩 알아보기로 하고 일단 의존성 주입이 되지 않은 코드부터 한번 살펴보자.
공식문서를 읽다가 느낀건데 의존성 주입을 하지 않은 것은 객체간의 결합도가 높은 것과 같다.
 
의존성 주입이 되지 않은 코드
class Car {
    private val engine = Engine()
    fun start() {
        engine.start()
    }
}

fun main(){
    val car : Car = Car()
    car.start()
}

class Engine {
    fun start(){
    }
}
위의 코드에서 문제점이 두가지 있다.
Car 클래스와 Engine 클래스의 결합도가 매우 높다는 점이다.
만약 Engine 말고도 SuperEngine, MotorEngine, UltraEngine 같은 새로운 객체들이 추가된다고 치면
Car 클래스는 이에 맞춰서 Engine을 갈아끼울 수 없다.
의역하면 다른 서브클래스나 구현체로 바꿔서 사용할 수 없다는 말이다.
Car 가 자신의 Engine 객체를 만들면, Car 객체 하나로 다른 타입의 Car 객체를 바꿔 넣을 수 없고 서로다른 두개의 Car 객체를 만들어야 한다.
 
  • The hard dependency on Engine makes testing more difficult. Car uses a real instance of Engine, thus preventing you from using a test double to modify Engine for different test cases.
위의 상황을 그림으로 표현하면 아래와 같다.

의존성 주입된 코드
1. 생성자에 의한 의존성 주입
class Car(private val engie : Engine) {
    fun start(){
        engie.start()
    }
}

fun main(){
    val engie = Engine()
    val car : Car = Car(engie)
    car.start()
}
이걸 의존성 주입이라고 한다. 의존성 주입이 되지 않았을때와는 다르게 생성자의 매개변수로 객체를 넣는다.
이렇게 하면 장점이 무엇이냐?
메인함수에서 Car 객체를 사용한다. Car 객체가 Engine 객체에 의존하기 때문에 앱은 Engine 객체를 만들고 이는 Car객체를 만들기 위해서 사용된다.
의존성 주입의 이점은 아래와 같다.
 
-Car 객체를 재사용할 수 있다. (다양한 Engine 구현체를 Car 객체에 담을 수 있다.)
-Car 객체를 테스트하기 쉬워진다. 다양한 구현체를 넣어서 코드가 동작하는지 확인할 수 있기 때문에 다양한 상황에서 메소드가 의도대로 동작하는지 파악하기 편하다.
 
의존성 주입을 했을때 상황은 아래 사진과 같다.

멤버변수를 활용한 의존성 주입
class Car() {
    lateinit var engine: Engine
    fun start(){
        engine.start()
    }
}

fun main(){
    val car = Car()
    car.engine = Engine()
    car.start()
}