의존대상 B가 변하면, 그것이 A에 영향을 미친다.
-- 이일민, 토비의 스프링 3.1, 에이콘(2012), p113
버거 셰프, 햄버거 레시피 관계로 Dependency 추상화 개념을 알아보자.
class BurgerChef {
private HamBurgerRecipe hamBurgerRecipe;
public BurgerChef() {
hamBurgerRecipe = new HamBurgerRecipe();
}
}
버거 셰프가 만들어야할 햄버거 레시피는 버거 셰프 생성자에 의해 자동으로 HamBurgerRecipe가 된다.
이 때, 버거 셰프는 햄버거 레시피에 의존한다.
다른 햄버거 레시피를 갖고 싶다면 new HamBurgerRecipe를 다른 햄버거 레시피로 하나하나 바꿔주어야 한다.
이 때, dependency interface 추상화가 필요하다.
예시로 설명하자면, 아래의 코드처럼 HamBurgerRecipe 타입 대신 여러 햄버거 레시피를 받을 수 있는 타입으로 interface를 생성해준 뒤 여러 햄버거 레시피를 받으면 된다.
class BurgerChef {
private BurgerRecipe burgerRecipe;
public BurgerChef() {
burgerRecipe = new HamBurgerRecipe();
//burgerRecipe = new CheeseBurgerRecipe();
//burgerRecipe = new ChickenBurgerRecipe();
}
}
interface BugerRecipe {
newBurger();
// 이외의 다양한 메소드
}
class HamBurgerRecipe implements BurgerRecipe {
public Burger newBurger() {
return new HamBerger();
}
// ...
}
BurgerRecipe로 inerface를 생성해 준 뒤, 여러 버거레시피를 BurgerRecipe를 implements하면서 다양하게 받을 수 있게 되었다.
하지만 레시피를 바꿀때마다 직접 버거 셰프 생성자를 수정해주어야 한다.
이 때, 의존성 주입이 등장한다.
: 의존 관계를 의존하고 있는 것들 내부에서 결정하는 것이 아니라 외부에서 결정하도록 하는 것
버거 셰프와 햄버거 레시피의 의존을 결정하는 햄버거 가게 주인이 의존성을 주입하는 주체가 된다.
class BurgerChef {
private BurgerRecipe burgerRecipe;
public BurgerChef(BurgerRecipe burgerRecipe) {
this.burgerRecipe = burgerRecipe;
}
}
class BurgerRestaurantOwner {
private BurgerChef burgerChef = new BurgerChef(new HamburgerRecipe());
public void changeMenu() {
burgerChef = new BurgerChef(new CheeseBurgerRecipe());
}
}
BurgerChef 생성자의 인자로 BurgerRecipe가 생긴 것을 알 수 있다.
이 BurgerRecipe는 햄버거 가게 주인에 의해 BurgerChef를 new하면서 인자로 HamBurgerRecipe를 주입한다.
따라서 BurgerChef는 생성자의 인자로 HamBurgerRecipe를 받게 된다는 말이 된다.
이렇게 직접 생성자에 주입해주는 방법이 있다.
class BurgerChef {
private BurgerRecipe burgerRecipe = new HamburgerRecipe();
public void setBurgerRecipe(BurgerRecipe burgerRecipe) {
this.burgerRecipe = burgerRecipe;
}
}
class BurgerRestaurantOwner {
private BurgerChef burgerChef = new BurgerChef();
public void changeMenu() {
burgerChef.setBurgerRecipe(new CheeseBurgerRecipe());
}
}
이번에는 햄버거 가게 주인이 BurgerChef에게 처음에는 HamburgerRecipe가 디폴트로 주어진다.
이 후, setBurgerRecipe라는 메소드를 사용해서 나중에 CheeseBurgerRecipe를 주입해준다.
- 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서는 인터페이스만 의존하고 있어야 한다.
- 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제3의 존재가 결정한다.
- 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어진다.
-- 이일민, 토비의 스프링 3.1, 에이콘(2012), p114
DI는 의존하는 객체를 외부에서 선언하고 주입하여 사용하는 것