Skip to main content

[오브젝트] Chapter09. 유연한 설계

· 9 min read

개방-폐쇄 원칙

소프트웨어 개체(클래스, 모듈, 함수 등등)은 확장에 대해 열려있어야 하고 수정에 대해서는 닫혀있어야 한다.
-- 마틴 파울러.

여기에는 '동작''코드' 의 관점을 반영한다,

  • 확장에 대해 열려있다. 애플리케이션의 요구사항이 변경될 때 이 변경에 맞게 새로운 '동작' 을 추가해서 애플리케이션의 기능을 확장할 수 있다.
  • 수정에 대해 닫혀있다. 기존의 '코드' 를 수정하지 않고도 애플리케이션의 동작을 추가하거나 변경할 수 있다.

컴파일 타임의 의존성을 고정시키고 런타임 의존성을 변경하라.

  • 런타임의 의존성은 실행시에 협력에 참여하는 객체들 사이의 관계이다.
  • 컴파일 타임의 의존성은 코드에 드러나는 클래스들 사이의 관계이다.

캡슐화 원칙을 지키는 객체는 꼭 필요한 퍼블릭 인터페이스만을 노출하기 때문에 인터페이스를 의존하는 객체들의 코드는 변경하지 않고
인터페이스를 따르는 새로운 객체를 추가하는 것으로 기능을 확장할 수 있다.

추상화가 핵심이다.

개방-폐쇄 원칙의 핵심은 추상화에 의존하는 것이다. 추상화에 의해 생략되지 않고 남겨지는 부분은 다양한 상황에서의 공통점을 반영한 결과이기 때문에 문맥이 바뀌더라도 변하지 않아야 한다. (수정에 닫힘.)
추상화에 생략된 부분은 확장의 여지를 남긴다. (확장에 열림.)

생성 사용 분리

소프트웨어 시스템은 (응용 프로그램 객체를 제작하고 의존성을 서로 "연결"하는) 시작 단계와 (시작 단계에 이어지는) 실행 단계를 분리해야 한다. -- 마틴 파울러

생성과 사용이라는 두 가지 이질적인 동작이 하나의 객체안에서 공존하게 되어 캡슐화를 약화시킨다.
이를 해결할 방법은 생성 책임을 객체 외부로 옮기는 것이다.

FACTORY 추가하기

FACTORY에 해당 객체의 생성 책임을 옮기면 생성되는 인스턴스를 사용하는 다른 객체의 생성 책임이 사라지고 사용에 필요한 정보만 알고있으면 되기 때문에 캡슐화가 강화된다.

순수한 가공물에 책임 할당하기 (PURE FABRICATION)

시스템을 객체로 분해하는 데에는 크게 두 가지 방법이 존재한다.

  • 표현적 분해
    도메인에 존재하는 사물 또는 객체들을 이용해 시스템을 분해하는 것이다.
    객체지향 설계를 위한 가장 기본적인 접근법이다.
  • 행위적 분해
    도메인 개념을 표현하는 객체에게 책임을 할당하는 것만으로 부족한 경우에 사용하는 방법이다. 책임을 할당하기위해 도메인과 무관한 객체를 생성하는 방법이다. 책임으로 인한 결합도를 낮추고 응집도를 높일수 있게 된다.

PURE FABRICATION 은 표현적 분해보다는 행위적 분해에 의해 생성되는 것이 일반적이다.
대부분의 디자인 패턴은 PURE FABRICATION 을 포함한다.

의존성 주입

외부의 다른 객체가 협력에 참여하는 객체에게 의존하고있는 정보를 넘겨주는 것을 의존성 주입 (Dependency Injection)이라고 한다.
의존성 주입에는 세 가지 방법이 있다.

  • 생성자 주입
    객체를 생성하는 시점에 생성자를 통한 의존성 해결
  • setter 주입
    객체 생성 후 setter 메서드를 통한 의존성 해결
  • 메서드 주입(메서드 호출 주입)
    메서드 실행 시 인자를 이용한 의존성 해결

숨겨진 의존성은 나쁘다.

SERVICE LOCATOR 패턴은 의존성 해결을 위한 제공자로서 활용된다. 깊은 계층의 객체에게 최단 경로로 의존성을 해결해 주지만, 의존성을 숨겨버리는 단점이 있다.

SERVICE LOCATOR 패턴은 서비스를 사용하는 코드로부터 서비스가 누구인지(서비스를 구현한 구체 클래스의 타임이 무엇인지), 어디에 있는지(클래스 인스턴스를 어떻게 얻을 지)를 몰라도 되게 해준다. -- Nystrom

의존성 역전 원칙

추상화와 의존성 역전

의존성 역전 원칙(Dependency Inversion Principle)은 다음과 같은 두 가지 원칙을 갖는다.

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

상위 개념의 객체의 변경으로 인해 하위의 객체가 변경되는 것은 납득할 수 있지만, 하위 객체의 변경으로 인해 상위 객체가 변경되서는 안된다.

의존성 역전 원칙과 패키지

서로 연관된 클래스를 같은 패키지 안에 두어야 한다. 서로 관련이 없는 클래스를 같은 패키지 안에 두는 것은 전체적인 빌드 시간을 가파르게 상승하게 한다. 이러한 기법을 SEPARATED INTERFACE 패턴이라고 부른다.

유연성에 대한 조언

유연한 설계는 유연성이 필요할 때만 옳다.

유연한 설계는 그 이면에 복잡한 설계란 의미를 내포하고있다. 불필요한 유연성은 불필요한 복잡성을 낳는다. 유지보수의 난이도는 자연스럽게 올라가고 소비되는 시간과 노력은 가파르게 상승한다. 설계는 언제나 트레이드오프의 산물임을 기억하자. 모든 설계에는 이유가 있어야 한다.

협력과 책임이 중요하다.

설계를 유연하게 만들기 위해서는 먼저 역할, 책임, 협력에 초점을 맞춰야 한다. 의존성을 관리해야 하는 이유는 역할, 책임, 협력의 관점에서 설계가 유연하고 재사용 가능해야 하기 때문이다.