티스토리 뷰

웹개발/Java

DIP 의존관계 역전원칙

야쿠 yaku 2010.12.30 13:40

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

 

Note

 DIP는 의존 관계 역전이라는 어려운 단어로 해석되고 있지만 실상은 간단하다. 구체화된 클래스를 추상화된 interface 또는 abstract 클래스의 구현 또는 상속 구조로 만들어서 실제 사용할 때 다음과 같은 코드로 사용하는 것으로 이해하면 좋을 것 같다.

 

  1. IFactory factory = new CheeseFactory();

 

  위에서 CheeseFactory는 IFactory 인터페이스를 구현하고 있음을 가정한 코드이다. 별로 신통해 보이지는 않지만 치즈공장에 버터공장, 마가린공장 등이 생겨났을 때 추상화된 클래스 타입으로 코드를 구성하는 것이 얼마나 도움이 되는지 확인할 수 있다. 위키피디아에서는 DIP가 다음과 같은 것을 만족해야 한다고 정의하고 있다.

 

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.

 

  구체화된 클래스는 그 기반이 되는 추상화된 클래스 타입으로 이용이 되도록 해야 한다는 정도로 이해하면 될 것 같다. 의존이나 역전이라는 단어에 의미를 두기 보다 확장이 가능하도록 상위 타입형태로 구체화된 클래스를 이용하라는 쪽으로 생각을 해보는 것이 좋겠다. 이렇게 이용하려 할 때 바로 떠오르는 것이 LSP가 되겠다. 구체화된 클래스가 추상화된 클래스에 정의된 멤버 메소드만 구현하고 있다면 타입을 파라미터로 전달하는 것만으로도 구체화된 클래스의 구현을 그대로 이용할 수 있다.

 

  1. public interface  IFactory {
  2. void make();

  3. }
  4.  
  5. public class CheeseFactory implements IFactory {
  6. public void make() {

  7. System.out.println("Make Cheese");

  8. }

  9. }

 

위와 같은 추상 클래스와 구체 클래스가 정의되어 있다면

  1. public class ClientOne {
  2. public static void main(String[] args) {

  3. IFactory factory = new CheeseFactory();

  4. factory.make();

  5. }

  6. }

 

  1. public class ClientTwo {
  2. public void someMake (IFactory factory) {

  3. factory.make();

  4. }

  5. public static void main(String[] args) {

  6. ClientTwo client = new ClientTwo();

  7. client.someMake(new CheeseFactory());

  8. }

  9. }

 위의 두 개의 클라이언트 코드와 같이 직접 생성해서 이용할 수도 있고, 메소드의 파라미터에 타입으로 받아 구체 클래스가 어떤 구현을 하고 있는지에 관계없이 이용이 가능하게 된다.

 

  LSP를 지키지 않는 코드라면 아마도 구체 클래스에 특화된 메소드를 이용하기 위해서는 형변환을 이용하는 방법밖에 없을 것이다. 한번 해보면 알겠지만 타입으로 받아 들인 인스턴스의 구체 클래스 타입을 확인하고 메소드를 실행해야 하는 상황은 코드를 엉망으로 만들고 어렵게 결합도를 떨어뜨린 보람도 느끼지 못하게 만들 것이다.

 

추가

 A. High-level modules should not depend on low-level modules. Both should depend on abstractions.

 

  위의 명제가 생각보다 많은 혼란을 일키는 것 같아 따로 정리하기로 한다. 먼저 위 문장을 직역해 본다면 상위 레벨의 모듈들은 하위 레벨의 모듈에 의존해서는 안되고, 둘다 추상화에 의존해야 한다. 라는 모호한 문장으로 해석될 수 있다. 이 원칙을 주장한 Robert C Martin은 이런 모호함에 대해 여러가지 모델을 가지고 설명하고 있다. 예로 서로 계층적인 관계를 가지는 세개의 컴포넌트에서 각 컴포넌트 사이에 추상화된 레이어를 추가하므로써 결합도를 낮추는 모델을 보여준다. 그리고 그 아래로 하위 모듈을 이용하는 두 개의 상위 모듈에 관한 모델과 C++에서 header 파일과 code 파일의 결합도를 떨어뜨리는 사례에 대해서도 보여주고 있다.

 

  위 문장을 모호하게 하는 가장 큰 원인이 모듈이라는 부분에 있을 것 같다. 모듈은 특정한 기능을 수행하는 하나의 단위로 시스템이 될 수도 있고, 어플리케이션이 될 수도 있고, 작게는 클래스 까지 포함할 수 있는 추상적인 단위이다. DIP의 유용성을 설명하기에는 각 모듈간의 결합도를 떨어뜨리는 모델을 보여주는 것도 효과적이지만 Dependency inversion에 대한 보다 간단하게(Simple) 정의할 수 있는 명제는 구체화된 것은 추상화된 것에 의존하는 것이 좋다. 까지만으로도 충분하다.

 

  이마저도 어려운 문장이기 때문에 이해하기 어렵다고 생각할 수 있지만 개발이나 설계에서의 기본원칙은 간단하다. "클래스는 추상화된 클래스 또는 인터페이스를 기반으로 작성한다." 이 하나의 문장만으로 두 DIP를 설명하는 두가지 명제를 모두 만족한다.

 

Keypoint
  • LSP를 지킬 것.

    • DIP를 지키기 위해서는 LSP를 지키는 것이 좋다.
  • 추상화된 기반 클래스를 통해 결합도를 떨어 뜨릴 것.

    • 클래스마다 인터페이스를 만드는 것은 비효율적이지만  확장을 염두에 둔다면 인터페이스를 기반으로 구체 클래스를 설계하는 것이 좋다.

 

 

 Reference

http://doortts.tistory.com/entry/OOP-의-원칙들-Object-Orientied-Programming-Principles

http://en.wikipedia.org/wiki/Object-oriented_analysis_and_design

http://www.aspiringcraftsman.com/2008/12/examining-dependency-inversion/

http://www.c2.com/cgi/wiki?DependencyInversionPrinciple

http://michaelfeathers.typepad.com/michael_feathers_blog/2010/05/class-splitting-and-the-graph-view-of-extract-method.html

 http://www.zdnet.co.kr/ArticleView.asp?artice_id=00000039137043

'웹개발 > Java' 카테고리의 다른 글

Building Java Projects with Maven  (0) 2016.01.16
알기쉬운 프로그래밍 설계 하기  (1) 2011.06.30
DIP 의존관계 역전원칙  (0) 2010.12.30
LSP 리스코프 치환의 원칙  (0) 2010.12.30
SRP 단일 책임의 원칙  (0) 2010.12.30
OCP 개방 폐쇄 원책  (0) 2010.12.30
댓글
댓글쓰기 폼