본 포스팅은 아래의 목적을 가집니다.
DI 는 무엇일까요?🧐
문자 그대로 의존성(종속성) 주입을 뜻합니다.
그럼 여기서 의존성은 무엇일까요?
의존의 사전적 정의는 아래와 같습니다.
사람에 빗대자면,
사람이 살아가기 위해서는 심장이 필요하다.
사람은 심장이 없으면 살 수 없으므로 '사람은 심장에 의존한다' 로 표현할 수 있습니다.
그렇다면, 의존성 주입이란 무엇일까요?
위에서 말한 심장 을 인공심장등으로 교체하는 행위를 뜻합니다.
(엣지러너의 산데비스탄 처럼??)
아래와 같은 요구사항이 있다고 가정합니다.
1. 정인이는 일할때 카페라떼를 마십니다.
이를 코드로 표현하자면 아래와 같습니다.
class JungIn {
val latte = CafeLatte()
fun work() {
latte.drink()
// ...
}
}
위 코드에서 JungIn
은 work
를 수행하기 위해 CafeLatte.drink()
를 실행해야 합니다.
이는 '클래스 JungIn
은 클래스 CafeLatte
에 의존성이 있다.' 로 표현할 수 있습니다.
이때 기획팀 의견으로 요구사항이 아래와 같이 변경되었습니다.
1. 정인이는 일할때 카페라떼를 마십니다.
2. 쿨라임 피지오도 마십니다.
이를 코드로 표현하면 CafeLatte
와 CoolLimePizzo
클래스에 의존성이 있는 JungIn
클래스가 정의됩니다.
interface Beverage {
fun drink()
}
class CafeLatte: Beverage {
override fun drink() {
print("yammy")
}
}
class CoolLimePizzo: Beverage {
override fun drink() {
print("soooooo good")
}
}
class JungIn {
val latte = CafeLatte()
val pizzo = CoolLimePizzo()
fun work() {
latte.drink()
pizzo.drink()
// ...
}
}
이때 우리는 아래와 같은 생각을 하게 됩니다.
'정인이가 다른 음료수를 마실수도 있겠구나!'😡
'음료수가 계속 추가되면 계속 수정해야겠네..'😇
그렇게 바뀐 코드는 아래와 같습니다.
class JungIn(private val beverages: List<Beverage>) {
fun work() {
beverages.forEach {
it.drink()
}
// ...
}
}
프로퍼티 CafeLatte
와 CoolLimePizzo
는 제거되고 음료수는 생성자를 통해 Beverage
interface 리스트로 넘겨받도록 변경되었습니다.
즉, CafeLatte
CoolLimePizzo
와의 강한 결합은 제거되고 Beverage
와의 느슨한 결합이 생성되었습니다.
이 느슨한 결합은 개발자가 매번 새로운 음료수를 프로퍼티로 작성할 필요도, 음료수의 생성자가 변경되어도 대응할 필요가 없는 유연한 코드 작성이 가능케 합니다.
이와같이 인스턴스를 클래스 내에서 생성하지 않고, 생성자 등으로 주입 받는 행위를 의존성 주입 이라고 합니다.
우리는 의존성 주입을 통해 유연하고 Testable 한 코드를 작성할 수 있습니다.
그럼.. 의존성 주입으로 유연한 코드 작성이 가능해지는 건 알겠는데, Testable 한 코드는 뭘까요??
--> 다음 포스팅에서 이어집니다..