SOLID > DIP (의존성 역전 원칙)
in etc.
본 글은 로버트 C.마틴 저자(송준이 엮음)의 [클린 아키텍처: 소프트웨어 구조와 설계의 원칙] 도서를 참고하였습니다.
1. 의미
- Dependency Inversion Principle.
- 프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다.
- 구체적으로 말하면, 고수준 정책을 구현하는 코드는 저수준 세부사항을 구현하는 코드에 절대로 의존해서는 안된다.
- 대신 세부사항이 정책에 의존해야 한다.
2. DIP 원칙 위반 예제
class OneHandSword {
final String NAME;
final int DAMAGE;
OneHandSword(String name, int damage) {
NAME = name;
DAMAGE = damage;
}
int attack() {
return DAMAGE;
}
}
class TwoHandSword {
// ...
}
class BatteAxe {
// ...
}
class WarHammer {
// ...
}
// ---
class Character {
final String NAME;
int health;
**OneHandSword weapon; // 의존 저수준 객체**
Character(String name, int health, OneHandSword weapon) {
this.NAME = name;
this.health = health;
this.weapon = weapon;
}
int attack() {
return weapon.attack(); // 의존 객체에서 메서드를 실행
}
void chageWeapon(OneHandSword weapon) {
this.weapon = weapon;
}
void getInfo() {
System.out.println("이름: " + NAME);
System.out.println("체력: " + health);
System.out.println("무기: " + weapon);
}
}
- Character 클래스는 인스턴스화될때 캐릭터 이름과 체력 그리고 장착하고 있는 무기를 입력값으로 받아 초기화함
- Character의 인스턴스 생성 시 OneHandSword에 의존성을 가지게 되어, 공격 동작을 담당하는 attack() 메소드 역시 OneHandSword에 의존성을 가지게 되게 됨
- 하지만 다른 무기들을 장착하려면, 아예 캐릭터 클래스의 클래스 필드 변수 타입을 교체해주어야 함
- 위 코드의 문제는 이미 완전하게 구현된 하위 모듈을 의존하고 있다는 점
- 즉, 구체 모듈을 의존하는 것이 아닌 추상적인 고수준 모듈을 의존하도록 리팩토링 해야 함
3. 수정한 예제
// 고수준 모듈
interface Weaponable {
int attack();
}
class OneHandSword implements Weaponable {
final String NAME;
final int DAMAGE;
OneHandSword(String name, int damage) {
NAME = name;
DAMAGE = damage;
}
public int attack() {
return DAMAGE;
}
}
class TwoHandSword implements Weaponable {
// ...
}
class BatteAxe implements Weaponable {
// ...
}
class WarHammer implements Weaponable {
// ...
}
// ---
class Character {
final String NAME;
int health;
**Weaponable weapon; // 의존을 고수준의 모듈로**
Character(String name, int health, Weaponable weapon) {
this.NAME = name;
this.health = health;
this.weapon = weapon;
}
int attack() {
return weapon.attack();
}
void chageWeapon(Weaponable weapon) {
this.weapon = weapon;
}
void getInfo() {
System.out.println("이름: " + NAME);
System.out.println("체력: " + health);
System.out.println("무기: " + weapon);
}
}
- 모든 무기들을 포함할 수 있는 고수준 모듈인 Weaponable 인터페이스를 생성
- 모든 공격 가능한 무기 객체는 이 인터페이스를 implements
- Character 클래스의 기존의 OneHandSword 타입의 필드 변수를 좀 더 고수준 모듈인 Weaponable 인터페이스 타입으로 변경
- 게임 시스템 내부적으로 모든 공격 가능한 무기는 Weaponable 을 구현하기로 가정했으므로, 공격 가능한 모든 무기를 할당 받을 수 있게 된 것
- DIP 원칙을 따름으로써, 무기의 변경에 따라 Character의 코드를 변경할 필요가 없고 또다른 타입의 무기 확장에도 무리가 없으니 OCP 원칙 또한 준수한 것
Reference
- 로버트 C.마틴, 2019, 클린 아키텍처: 소프트웨어 구조와 설계의 원칙
- https://ko.wikipedia.org/wiki/SOLID(객체지향_설계)
- https://inpa.tistory.com/entry/OOP-💠-아주-쉽게-이해하는-DIP-의존-역전-원칙