위키백화에서는 “프로그래밍에서 구성요소간의 의존 관계가 소스코드 내부가 아닌 외부의 설정파일 등을 통해 정의되게 하는 디자인 패턴 중의 하나이다.”라고 정의되어 있다. 의존성 주입이란 특정 객체의 인스턴스가 필요한 경우 외부에서 생성하여 전달하는 기법이다. 즉 외부에서 객체를 생성하여 그 객체를 넘겨 받는 것을 뜻한다.
Class A 가 Class B를 의존한다면 Class A가 직업 Class B를 생성하는것이 아니라 외부에서 Class B를 생성하여 넘겨 받는 것을 의존성 주입이라고 생각하면 된다.
Class A에서 직접 Class B 생성하여 의존하는 방식
외부에서 Class B를 성생하여 Class A에 주입하여 Class A가 의존하는 방식
의존성 파라미터를 생성자에 작성하지 않아도 사용할 수 있다. 즉 보일러 플레이트 코드*를 줄일 수 있다.
(보일러 플레이트 코드 - 최소한의 변경으로 여러곳에서 재사용되며, 반복적으로 비슷한 형태를 띄는 코드)
코드간 커플링을 감소시킬수 있고 재사용 및 유지보수를 용이하게 할 수 있습니다
유닛 테스트 시에도 간결한 구현을 가능하게 합니다
코드의 재사용성 증대
리팩토링이 수월해진다
클래스간의 결함도를 줄일수 있다
스코프를 이용해서 객체 관리를 할 수 있다.
단, 의존성 주입은 선행작업이 필요하고 코드르 추적하고 읽는데 어려움이 있다.
Dagger 공식 문서에서는 “Dagger is a fully static, compile-time dependency injection framework for both Java and Android.”라고 Dagger를 정의하고 있다. 즉 의존성 주입을 도와주는 프레임워크이다. Dagger는 의존성 주입을 구현하는데 사용되는 라이브러리이다.
이 예제는 '김태호의 커니의 코틀린'에서 가져왔습니다.
햄버거를 만들어 봅시다.
이 햄버거는 밀빵(WheatBun)과 소고기 패티(BeefPatty)로 이루워져있다.
public class Burger {
public WheatBun bun;
public BeefPatty patty;
public Burger(WheatBun bun, BeefPatty patty) {
this.bun = bun;
this.patty = patty;
}
}
public class WheatBun {
public String getBun() {
return "밀빵";
}
}
public class BeefPatty {
public String getPatty() {
return "소고기 패티";
}
}
일반적으로 햄버거를 MainActivity에서 생성해보자.
public class MainActivity extends AppCompatActivity {
Burger burger;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WheatBun bun = new WheatBun();
BeefPatty patty = new BeefPatty();
burger = new Burger(bun, patty);
}
}
이와 같이 각 필요한 부분들을 새로 생성하여 햄버거를 생성했씁니다. 즉 각 인스턴스를 생성해서 만드는 방식입니다. 이제는 의존성 주입을 사용하여 외부에서 자동으로 객체를 생성해서 만들어 봅시다.
Dagger2를 사용하기 위해서는 우선 build.gradle에 라이브러리를 추가해야한다. (최신 버전은 공식 문서에서 확인하면 된다.)
implementation "com.google.dagger:dagger:$daggerVersion"
implementation "com.google.dagger:dagger-android:$daggerVersion"
implementation "com.google.dagger:dagger-android-support:$daggerVersion"
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
첫번째로는 Module를 생성해야한다. Module은 필요한 객체를 생성해준다. 생성할 객체는 @Provides 어노테이션을 통해 생성한다.
@Module
public class BurgerModule {
@Provides
Burger provideBurger(WheatBun bun, BeefPatty patty) {
return new Burger(bun, patty);
}
@Provides
WheatBun provideBun() {
return new WheatBun();
}
@Provides
BeefPatty providePatty() {
return new BeefPatty();
}
}
두번째로는 Component를 생성한다. Component는 Module을 불러오고 객체를 조합하여 필요한 곳에 주입하는 역할을 한다. @Component 를 통해 BurgerModule를 불러온다. 그리고 inject()를 통해 원하는데 곳에 주입한다.
@Component(modules = BurgerModule.class)
public interface BurgerComponent {
void inject(MainActivity activity);
}
component를 작성하면 자동으로 DaggerBurgerComponent(Dagger + Component 이름)라는 Dagger 파일이 생성된다. 이제 MainActivity에서 Burger를 의존성 주입을 통해 생성해보자.
@inject는 의존성 주입을 요청하는 역할을 한다. inject를 통해 주입을 요청하면 연결된 component가 module로부터 객체를 생성하여 넘겨준다. inject 어노테이션은 주입을 받을 곳을 정한다.
public class MainActivity extends AppCompatActivity {
@Inject
Burger burger;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BurgerComponent component = DaggerBurgerComponent.builder()
.burgerModule(new BurgerModule())
.build();
component.inject(this);
Log.d("MyTag","burger bun : " + burger.bun.getBun() +
" , patty : " + burger.patty.getPatty());
}
}