📔
Today Joonas Learned
  • Home
  • About Me
  • Chrome Extension
    • CSS injection
  • Design Pattern
    • SOLID 원칙
      • 1. SRP
      • 2. OCP
      • 3. LSP
      • 4. ISP
      • 5. DIP
    • 생성 패턴
      • Singleton Pattern
      • Abstract Factory Pattern
      • Factory Method Pattern
    • 구조 패턴
      • Adapter Pattern
      • Bridge Pattern
      • Composite Pattern
      • Decorator Pattern
      • Facade Pattern
      • Proxy Pattern
    • 행위 패턴
      • Command Pattern
      • Observer Pattern
      • State Pattern
      • Strategy Pattern
      • Template Method Pattern
  • Graphics
    • OpenGL ES
      • 파이프라인
      • 삼각형 그리기
      • 삼각형 움직이기
      • 다각형 그리기
      • 정사면체 그리기
      • [WIP] 마인크래프트 블럭 만들기
      • [WIP] Lighting, Normal Mapping
  • Internet
    • iOS/Safari
  • Javascript
    • async, defer 속성
    • 나머지 매개변수 (Rest parameter)
    • 화살표 함수 표현 (arrow function expression)
    • Template Literals
    • TDZ (Temporal Dead Zone)
    • Spread syntax (...)
  • Network
    • OSI 7 계층 모델
  • Uncategorized
    • 2021/12/07
    • 2020/09/03
    • 2020/09/04
    • 2020/08/22
  • git/VCS
    • Merge 커밋 메시지 수정
Powered by GitBook
On this page
  • Dependency Inversion Principle (DIP; 의존관계 역전 원칙)
  • 위반 사례
  • 해결
  • 전체 코드
  • Links

Was this helpful?

  1. Design Pattern
  2. SOLID 원칙

5. DIP

Dependency Inversion Principle (DIP; 의존관계 역전 원칙)

Previous4. ISPNext생성 패턴

Last updated 4 years ago

Was this helpful?

Dependency Inversion Principle (DIP; 의존관계 역전 원칙)

프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다.

더 자세히는 이렇게 말한다.

상위 계층(정책 결정)이 하위 계층(세부 사항)에 의존하는 전통적인 의존관계를 반전(역전)시킴으로써 상위 계층이 하위 계층의 구현으로부터 독립되게 할 수 있다.

  1. 상위 모듈은 하위 모듈에 의존해서는 안된다. 상위 모듈과 하위 모듈 모두 추상화에 의존해야 한다.

  2. 추상화는 세부 사항에 의존해서는 안된다. 세부사항이 추상화에 의존해야 한다.

의존 관계가 많을수록 코드의 변경이 잦아지는 것은 당연하다. 변경할 곳이 많다는 의미는 코드를 파악하고 수정하는 일이 무척 어렵다는 뜻이다. 의존 관계에 신경을 쓰는 이유는 이런 부분이다.

추상클래스나 인터페이스는 구현 클래스보다 덜 변화되기 때문에 추상화에 신경써서 의존도를 낮추라는 뜻이다.

위반 사례

여기 버튼(Button)과 전등(Lamp)이 있다.

Button은 외부 환경을 감지하는 객체이다. 버튼을 눌렀는지 여부만 감지할 뿐이다.

Lamp는 외부 환경을 변화시키는 객체이다. turnOn 메소드가 호출되면 전등을 켜서 불을 밝힌다.

Button을 눌러서 Lamp를 동작하게 하려면 어떤 구조여야 할까.

Lamp에 Button을 추가하면 해결될까?

class Lamp {
  private Button button;
  public void turnOn();
  public void turnOff();
}

동작은 하겠지만 이 방법은 의존 관계 역전 규칙(DIP)을 위반한다. 구체적인 클래스인 Lamp에서 Button을 포함하기 때문이다.

지금의 Button은 Lamp의 구현에 완전히 묶여있다. Button 이 필요한 모든 클래스(Computer, TV 등..)에서 전부 선언할 수는 없는 노릇이다.

해결

이제 Button와 Lamp 는 추상화 된 모듈을 의존한다. Lamp에서 코드가 바뀌더라도 서로 영향을 받지 않는다.

Button은 Switchable 인터페이스에 의존하고, Lamp가 Switchable의 인터페이스를 구현하게 되면 Lamp가 Button에 의존하는 역전 관계가 되는 것이다.

이렇게 되면, Switchable의 인터페이스를 조작하는 방법을 아는 객체는 Lamp를 켜고 끌 수 있다.

interface Switchable {
  void activate();
  void deactivate();
}

class Lamp implements Switchable {
  @Override public void activate() {
    System.out.println("Lamp: turn on");
  }
  
  @Override public void deactivate() {
    System.out.println("Lamp: turn off");
  }
}

Lamp는 이제 환경에 상관없이 자신의 동작인 activate와 deactivate 구현에 집중할 수 있다.

이 부분에는 어댑터 패턴(Adapter Pattern)이 사용되었다.

abstract class Button {
  private Switchable client;

  public Button(Switchable client) {
    this.client = client;
  }

  // detect its state and activate/deactivate  
  public void run() {
    boolean isButtonOn = getState();
    if (isButtonOn) {
      client.activate();
    } else {
      client.deactivate();
    }
  }

  abstract boolean getState();
}

Button도 구현체에 상관없이 외부 환경을 감지하여, 참조하는 객체에게 어떤 동작을 일으킬 수 있게 되었다.

class ToggleButton extends Button {
  private boolean isActive = false;
  
  public ToggleButton(Switchable client) {
    super(client);
  }

  // Unique method only for ToggleButton
  public void toggle() {
    this.isActive = !this.isActive;
  }

  @Override public boolean getState() {
    return this.isActive;
  }
}

Button의 구현 중 하나인 ToggleButton은 toggle() 을 호출하면 버튼의 ON/OFF 상태가 반전된다.

상태 변화만 다루고 있으며, Lamp 따위의 동작에는 어떤 개입도 하지 않는 것을 확인할 수 있다.

class Main {
  public static void main(String[] args) {
    ToggleButton lampButton = new ToggleButton(new Lamp());
    lampButton.run();
    lampButton.toggle();
    lampButton.run();
  }
}

ToggleButton에 Lamp를 위처럼 연결하였다. Button.run() 을 호출하면 Button 의 상태에 따라 Lamp 를 동작시키고 있다.

같은 방식으로 객체 Computer 도 추가할 수 있다.

class Computer implements Switchable {
  @Override public void activate() {
    System.out.println("Computer: turn on");
  }

  @Override public void deactivate() {
    System.out.println("Computer: turn off");
  }
}

class Main {
  public static void main(String[] args) {
    ToggleButton computerButton = new ToggleButton(new Computer());
    computerButton.run();
    computerButton.toggle();
    computerButton.run();
  }
}

전체 코드

Links

에서는 Button도 추상화하였다. 구현 클래스와 추상 클래스를 완전히 분리시킨 것이다.

Robert C. Martin의 레포트
https://ko.wikipedia.org/wiki/SOLID_(객체_지향_설계)
https://walbatrossw.github.io/oop/2018/07/27/06-solid-dip.html
The Dependency Inversion Principle, Robert C. Martin, C++ Report, May 1996
https://blog.naver.com/jwyoon25/221776517812
http://stg-tud.github.io/sedc/Lecture/ws13-14/3.5-DIP.html
forked from C++ Report by Robert C. Martin