Objects 9장 유연한 설계

카일·2020년 3월 2일
1

Objects 오브젝트

목록 보기
9/11
post-thumbnail

유연한 설계

개방-폐쇄 원칙

개방 폐쇄 원칙(Open-Closed-Principle)은 한 마디로 요약하면 소프트웨어 개체는 확장에 대해 열려있어야하며 수정에 대해서는 닫혀 있어야한다는 의미이다. 어떻게 확장에는 열려있으면서(요구사항이 변경될 때 이 변경에 맞게 새로운 동작을 추가해서 기능을 확장하는 것) 수정(기존의 코드를 수정하지 않고도 어플리케이션의 동작을 추가하거나 변경)에는 닫혀 있을 수 있을까? 앞장에서 다루었던 내용과 비슷하다고 느껴지지 않는가?

  • 컴파일타임 의존성을 고정시키고 런타임 의존성을 변경하라
    • 개방 폐쇄 원칙은 결국 컴파일타임에서의 의존성을 변경하지 않고 런타임 의존성을 쉽게 변경할 수 있다.
    • 가격할인 정책이 새로운 것이 추가되더라도 우리는 추상화된 인터페이스를 의존하고 있기 때문에 새로운 클래스를 추가함으로써 기존 코드의 변경없이 기능을 확장할 수 있다.
  • 추상화가 핵심이다.
    • 결국 개방 폐쇄 원칙의 핵심은 추상화에 의존하는 것을 의미한다. 추상화에 의존함으로써 구체적인 코드에 강하게 결합되지 않고 이는 유지보수를 용이하게 만들 뿐 아니라 새로운 변경사항에 대해서도 기존코드 변경없이 추상화를 구현하고 있는 새로운 클래스를 추가함으로써 개방폐쇄원칙을 가능하게 한다.

생성 사용 분리

생성

위의 개방 폐쇄 원칙에 따라 우리는 생성자를 통해 런타임에서 사용될 의존성을 주입받아 사용한다. 즉 내부적으로 특정한 구현체를 생성하여 사용하는 것이 아니라 외부에게 그 책임을 전가한다. 이는 내부에서 구체적인 구현체를 생성하는 것은 개방폐쇄의 원칙을 위반하고 구체적인 클래스에 결합됨으로써 결합도가 높아지기 때문이다. 이를 방지하기 위해 우리는 외부에게 그 책임을 전가하였다.

그렇다면 외부의 관점에서는 어떤가? 일반적으로 이러한 생성을 담당하는 부분은 클라이언트다. 클라이언트는 객체를 생성하고 생성된 객체를 바탕으로 특정한 메세지를 통해 자신이 원하는 것을 얻는다. 전 문장에서 이상한 부분이 있었는가? 클라이언트는 객체를 생성 하고 사용 한다. 즉 클라이언트의 역할은 두 개라는 것이다. 협력에 참여하는 모든 객체는 SRP에 의해 하나의 역할을 수행하는 것이 바람직한데 현재 클라이언트는 두가지 역할을 수행하고 있는 것이다. 그렇다면 이를 생성자내부로 옮기면 위와 같은 문제가 다시 발생한다.

사용

어떻게 할 것인가? 순수한 가공물에게 책임을 할당하자. 순수한 가공물이란 순수하게 기술적인 결정으로 프로그램 전체적으로 결합도를 낮추고 재사용성을 높이기 위해 도메인 개념에게 할당되어 있던 객체 생성 책임을 도메인 개념과는 아무런 상관없는 가공의 객체로 이동한 것이다. 즉 도메인의 영역이 아니라 프로그램 전반을 위해 필요한 기능을 수행하는 객체인 것이다.

이렇게 순수한 가공물을 사용하면 프로그램간의 결합도는 낮아지고 응집성은 높아질 수 있다. 흔히 우리가 사용하는 FACTORY 가 이러한 순수한 가공물에 속한다. 특정한 상태를 가지지 않고 생성만들 담당하고 있는데 이는 클라이언트가 두가지 역할을 하는 것을 방지하며 프로그램 전반에서의 결합도를 낮추기 위한 하나의 방안이다.

의존성 주입과 의존성 역전

의존성 주입

의존성 주입이란 사용하는 객체가 아닌 외부의 독립적인 객체가 인스턴스를 생성한 후 이를 전달해서 의존성을 해결하는 방법을 의존성 주입이라고 한다. 이를 의존성 주입이라 부르는 이유는 외부에서 의존성의 대상을 해결한 후 이를 사용하는 객체 쪽으로 주입하기 때문이다. 외부에서 의존성을 주입하는 방법에는 크게 3가지가 존재하는데

  • 생성자 주입
  • Setter 주입
  • 메서드 주입

참고로 의존성을 외부에 드러내는 것은 바람직하다. 외부에서 필요로 하는 의존성을 모른다면 객체 내부의 구현에 대해서 직접 찾아보아야 하기 때문에 외부에게 의존성을 노출하여 필요한 의존성을 적절히 주입받는 방식이 바람직하다. 물론 의존성을 숨기고 의존성을 따로 설정하는 방법이 존재하지만 이는 특수한 경우가 아니면 바람직하지 않다.

의존성 역전

의존성 역전의 원칙이란 아래의 두가지를 충족할 때 불리어지는 이름이다. 이를 의존성의 역전이라고 부르는 이유는 기존의 프로그래밍 패러다임에서는 하위 수준의 모듈이나 구체적인 사항에 의존하는 것이 당연시 되었다. 이러한 현상이 역전되었다는 의미에서 추상화 수준에 의존하는 것을 의존성 역전이라고 부른다.

  • 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안된다. 둘 모두 추상화에 의존해야 한다.
  • 추상화는 구체적인 사항에 의존해서는 안된다. 구체적인 사항이 추상화에 의존해야한다.

유연성에 대한 조언

유연하고 재사용 가능한 설계란 런타임 의존성과 컴파일타임 의존성의 차이를 인식하고 동일한 컴파일 타임 의존성으로 부터 다양한 런타임 의존성을 만들 수 있는 코드 구조를 가지는 설계를 의미한다.

하지만 항상 유연하고 재사용 가능한설계가 좋은 것은 아니다. 설계의 미덕은 단순함과 명확함에서 비롯된다. 유연하고 재사용가능한 설계는 단순하고 명확한 설계와 거리가 멀어질 확률이 높다. 즉 유연하고 재사용할 수 있는 설계는 이러한 설계가 필요하다는 확실한 근거 아래에서 의미 있는 것이다.

모든 것은 객체간의 협력의 관점에서 역할을 정의하는데 있어서 비롯되어야 한다. 다양한 할인 정책은 할인한다라는 역할을 만족하기 위해 다양한 객체가 존재할 수 있음을 명확하게 확인하였기에 이러한 설계는 의미있었을 뿐 단순히 추상화 했다고 좋은 설계라고 할 수 없다.

객체의 생성을 고민하기전에 협력의 관점에서 객체들을 바라보자. 협력의 관점에서 필요하지 않은 설계는 무의미한 설계일 뿐이다.

0개의 댓글