프로그래머는 구체화가 아니라 추상화에 의존해야 한다.
쉽게 이야기하면, 구체 클래스가 아니라 인터페이스에 의존하라는 말이다.
자동차라는 역할(인터페이스)이 있고 이를 구체화한 자동차A, 그리고 운전자(클라이언트)가 있다고 하자. 운전자가 자동차에 대해서는 모르고 자동차A만 알고 있다면 자동차B, 자동차C 등의 다른 자동차를 운전하게 될 경우 각각 사용법을 다시 배워야 한다.
자동차 인터페이스
public interface Car {
void accelerate();
void brake();
void gearBox(int mode);
...
}
자동차 구현체
public class K3 implement Car {
...
}
public class Avante implement Car {
...
}
클라이언트 (사용자)
public class User {
Car car = new K3();
...
}
위의 코드는 DIP를 위반한 코드이다.
클라이언트는 Car 인터페이스의 구현체로 K3를 생성하고 있다.
즉, 클라이언트가 어떤 구현체를 쓸 것인지 알고 있다는 것이다.
다르게 말하면 클라이언트가 K3라는 구현체에 의존하고 있다고도 표현할 수 있다.
이렇게 되면 나중에 다른 구현체로 바꾸어야 할 때 new Avanate()
라고 직접 클라이언트의 코드를 변경해야 한다. 구현체가 바뀔때마다 클라이언트 코드의 변경이 불가피하다는 뜻이다.
클라이언트가 구현체가 아닌 추상화에 의존하게되면 어떻게 되는지 보자.
public class User {
Car car;
public User (Car car) {
this.car = car;
}
}
클라이언트가 직접 구현체를 생성하지 않고 생성자를 통해 외부에서 구현체를 받도록 했다.
이제 클라이언트는 어떤 구현체를 쓰게 될지 모르고 구현체가 바뀔때마다 코드를 변경할 필요가 없어졌다.
이처럼 외부에서 구현체를 받는 것을 의존성 주입(Dependency Injection)이라고 한다.
DIP를 지키게 되면 코드 변경을 최소화할 수 있다는 장점이 있다.
코드의 유연성과 확장성을 향상시킬 수 있고 이는 생산성 향상으로도 이어진다.
그 밖에도 재사용성 증가, 테스트 용이 등등 여러 가지 장점들이 존재한다.