SOLID 원칙 - (5) 의존 역전 원칙 (DIP)



(5) 의존 역전 원칙 (DIP)

👴 Depend in the direction of abstraction. High level modules should not depend upon low level details.

SOLID의 마지막 원칙인 의존 역전 원칙은 저수준 모듈이 고수준 모듈에 의존해야 한다는 것이다. UML에서 추상화의 방향은 저수준 -▷ 고수준으로 되어있다. Depend in the direction of abstraction이라는 말은 이러한 방향을 따라야 한다는 말인 것 같다. 이렇게 한다면 저수준의 모듈이 변경되더라도 고수준 모듈은 변경될 필요가 적어진다.
그렇다면 고수준과 저수준이란 말의 의미는 무엇일까? 통상적으로 고수준은 의미 있는 기능을 제공하는 모듈을 말하고 저수준은 고수준의 모듈을 제공하기 위해 필요한 모듈을 말한다.
고수준이 저수준에 의존하지 않고, 저수준이 고수준에 의존하게 하는 것은 추상화를 통해 실현할 수 있다. 아래 예시를 보자.


DIP 예시

OCPLSP에서의 예시와 비슷한 예시를 이용해보자.


(상황 ) 롤에서 한타라는 중요한 순간에 스킬 콤보를 넣는 것은 중요하다. 하지만 캐릭터마다 스킬이 다르기때문에 콤보도 다 다르다. 따라서 캐릭터마다 콤보를 다 다르게 설정해주어야 한다.


EX1) 고수준이 저수준에 의존하는 경우

img1

첫번째로 한타라는 고수준 모듈이 캐릭터라는 저수준 모듈에 의존한다고 해보자.

public void 한타(Character character) {
      if(character instanceof 야스오){
          character.Q(); character.E(); character.Q(); character.R();
      }
      else if(character instanceof 탈론){
          character.W(); character.Q(); character.R();
      }
      else {
          ...
      }
  }
}

고수준이 저수준에 의존하고 있으므로 저수준 모듈이 추가될때마다 고수준 모듈은 계속 변경되어야 한다. 위와 같은 코드에서 캐릭터가 100명이 추가된다고 하면 if-else 블록이 100개가 되는 것이다.


EX2) 저수준이 고수준에 의존하는 경우

img2

저수준이 고수준에 의존하도록 바꾼다면 어떻게 될까? 앞서 말했듯 추상화를 이용했다. 인터페이스를 이용해 추상화하는 방법과 추상클래스를 이용해 추상화하는 방법 두가지를 사용해보았다.

// 1. 인터페이스 이용
public interface Combo() {
		public void combo();
}
public class Character implements Combo {}
public class 야스오 extends Character {}
public class 탈론 extends Character {}
// 2. 추상 클래스 이용
public abstract class Character {
		public abstract void combo();
}
public class 야스오 extends Character {}
public class 탈론 extends Character {}
// 고수준 모듈
public void 한타(Character character) {
		character.combo();
}

이제 캐릭터라는 저수준 모듈이 변경되어도 한타라는 고수준 모듈은 변경될 필요가 없다. 결국 기능의 변경으로 인해 그 기능을 이용하는 곳의 변경은 최소화해야 한다는 개방 폐쇄 원칙과도 관련이 있다.


❗ 주의 ❗ 런타임에서의 의존이 바뀌는 것은 아니다

다만 주의할 점으로는 런타임에서의 모듈간의 의존 방향이 바뀌는 것은 아니라고 한다. 의존 역전 원칙은 소스코드의 의존을 역전시켜서 프로그래머가 변경에 유연하게 대처할 수 있도록 하는 원칙이지, 런타임에서의 의존을 역전시키는 것은 아니다.


결론

SOLID 원칙에서 SRP, ISP와 LSP, IDP, OCP는 묶어서 볼 수 있다.

SRP와 ISP는 객체의 크기가 커지지 않도록 한다. 한 곳에서의 변경이 다른 곳에 미치는 영향을 최소화한다.
LSP, IDP는 OCP를 지원한다. 추상화와 다형성을 이용해 기능을 확장하면서도 기존 코드의 수정을 최소화한다.

다만 SOLID 원칙은 소스코드를 사용하는 사용자 입장에서의 기능 사용에 중점을 둔다. LSP는 사용자에게 기능 명세를 제공한다. ISP는 클라이언트 입장에서 인터페이스를 분리한다. DIP는 고수준 모듈 입장에서 추상화를 도출해낸다.

SOLID 원칙을 잘 지켜서 프로그래밍 해보도록 하자..!😤



:bookmark: REFERENCE
최범균, 「개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴」, 인투북스
Solid Relevance