Review: 소프트웨어 개발의 지혜 - 11장_의존 관계 역전 원칙

백근영·2020년 3월 28일
0
post-thumbnail

DIP : 의존 관계 역전 원칙

a. 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안된다. 둘 모두 추상화에 의존해야 한다.

b. 추상화는 구체적인 사항에 의존해서는 안 된다. 구체적인 사항은 추상화에 의존해야 한다.

DIP의 이름이 DIP인 이유

DIP는 물론 매우 유익한 내용을 담고 있는 객체 지향 설계 원칙이지만, 그 이름에 왜 역전(inversion)이라는 단어가 사용되는지 단번에 와닿지 않을 것이다.(나도 이 책을 읽고 나서야 그 이유를 알게 되었다.) 이는 보다 전통적인 소프트웨어 개발 방법에서는 소프트웨어 구조에서 상위 수준의 모듈이 하위 수준의 모듈에 의존하는 경향이 있었기 때문이다. DIP에서 지향하는 것은 오히려 하위 수준의 모듈이 상위 수준 모듈에 의존하게 되는 것이므로, 의존 관계 역전이라는 이름이 붙을 만하다.

DIP를 지켜야 하는 이유

DIP를 지켜야 하는 근본적인 이유를 알기 위해선 상위 수준의 모듈이 하위 수준의 모듈에 비해서 가지는 의미와 중요성을 인식해야 한다. 상위 수준의 모듈은 애플리케이션의 중요한 정책 의사결정과 비즈니스 모델을 포함하고 있다. 이것이 하위 수준 모듈에 비해 가지는 차이점이며, 우리는 이러한 상위 수준의 모듈을 재사용하기를 원한다. 따라서 상위 수준의 모듈은 타 모듈에 가지는 의존성을 최소화하고 최대한 독립적으로 유지하길 바라는 것이며, DIP는 이 목적을 달성하는 데 큰 도움을 준다.

아래로 향하는 의존성이 위험한 이유

DIP를 지키지 않고 의존성이 아래로 향하는, 즉 상위 수준 모듈이 하위 수준 모듈에 의존하는 형태가 위험한 이유는 의존성이 이행적(transitive)인 성격을 띠기 때문이다.

위 구조는 기능별로 layer의 분리는 확실히 되어 있지만, transitive한 의존성으로 인해 가장 상위의 모듈인 policy layer는 하위 모듈의 변경에 상당히 취약하게 된다. 결국 힘겹게 layer를 분리해서 얻는 이점이 사라질 것이다.

추상화에 의존하라

DIP의 해석에 대한 간단한 휴리스틱은 '추상화에 의존하자'이다. 쉽게 말해 구체적인 것에 의존하지 말고, 의존하는 것과 의존받는 것 사이에 추상 인터페이스를 하나 두라는 말이다. 이 휴리스틱에 의하면,

  • 어떤 변수도 구체적 클래스에 대한 포인터나 참조값을 가져서는 안된다.
  • 어떤 클래스도 구체적 클래스에서 파생되어서는 안된다.
  • 어떤 메소드도 그 기반 클래스에서 구현된 메소드를 오버라이드해서는 안된다.

물론 이 휴리스틱은 어플리케이션 내에 존재하는 모든 객체에 대해 지켜지기란 힘들다. 구체적이긴 하지만 비휘발적인 클래스에는 이 휴리스틱을 적용시킬 이유가 없어보인다. 예를 들어 String과 같은 클래스는 구체적이긴 하지만 비휘발적이고, 변경될 여지가 거의 없을 것이다. 그러므로 객체들이 String 클래스의 멤버 변수를 가지는 것은 전혀 문제가 되지 않는다.

소유권의 역전

DIP에서 말하는 역전은 의존 관계에 대한 것이기도 하지만, 소유권에 관한 것이기도 하다. 위와 같이 구체적인 것에 의존하지 않고 인터페이스에 의존하도록 의존 관계를 역전하면 아래와 같이 자연스럽게 상위 모듈이 자신이 의존하는 클래스의 인터페이스를 소유하는 경향을 띠게 된다.

상위 모듈은 자신이 의존하는 모듈의 인터페이스를 직접 소유함으로써 크게 보았을 때 의존성이 아래로 향하는 것을 막을 수 있고, 반대로 하위 모듈은 그 인터페이스를 구현하는 방식으로 상위 모듈에 의존하기 때문에 의존성의 방향이 위로 향하게 된다.

간단한 예

DIP를 설명하기 위한 예시로 Button 클래스와 Lamp 클래스 간 의존 관계를 설계해보자. Lamp 객체는 Button에 의해 제어되며, button으로 lamp를 끄거나 켜거나 할 수 있다. naive하게 Button이 자신이 제어해야 할 Lamp에 직접 의존한다면, 이는 당연히 DIP를 위반할 것이다.

내재하는 추상화를 찾아서

우리는 고객의 요구사항으로부터 내재하는 추상화를 찾아내야 한다. 내재하는 추상화는 즉 구체적인 것들이 변경되더라도 바뀌지 않는 진실을 뜻한다. 이러한 도메인 영역의 핵심적인 내용들만을 상위 수준 모듈에 남기고, 다른 모든 부분들은 구체적인 것이 아닌 추상화에 의존할 수 있도록 DIP를 적용해보자.

위 예시에서 내재하는 추상화는 "사용자로부터 켜고 끄는 동작을 탐지해 그 동작을 대상 객체에 전해주는 것"이다. 이외의 모든 것들은 언제든 바뀔 수 있는 구체적인 것들이다. 다시 말해 버튼이 사용자로부터 동작을 어떤 메커니즘을 통해 탐지할 지, 동작을 전해줄 객체가 전구인지 텔레비전인지 컴퓨터인지는 이 도메인 영역의 핵심과 전혀 관계가 없다는 것이다. 그래서 우리는 DIP를 통해 아래와 같은 구조를 만들 수 있을 것이다.

DDD에서 접했던 '심층 모델로의 도약'과 비슷한 얘기를 하고 있다. 심층적인 도메인 모델을 만들기 위해 끊임없이 노력해야 한다는 부분이 조금은 막연하게 느껴졌었는데, 구체적으로 DIP와 관련지어서 생각해볼 수 있어서 DDD에서 의미하는 바가 무엇이었는지 조금 더 깊이 생각할 수 있게 되었다.

profile
서울대학교 컴퓨터공학부 github.com/BaekGeunYoung

0개의 댓글