좋은 객체지향 설계

객체 지향 소프트웨어를 설계하기란 쉬운 일이 아니다. 좋은 설계가 되기 위해서는 아래와 같은 요소가 필수적이다.

  • 유지보수성
  • 기능확장

소프트웨어는 개발 단계에서도 요구사항의 변경이 있을 수 있으며, 서비스 중에도 끊임없이 기능 확장이나 유지보수가 이루어진다. 따라서 소프트웨어는 재설계를 하지 않아도 다시 사용할 수 있는 유연성을 제공해야 한다.

역사는 반복된다.

프로그래밍을 하다보면 비슷한 문제를 반복적으로 마주하게 되고 이것을 해결하는 방법 또한 반복적으로 사용된다는 것을 인지하게 된다. 이미 경험한 문제를 다시 겪는다면 이전 경험을 통해 비교적 쉽게 해결할 수 있다. 이를 통해서 반복되는 설계 문제를 해결하는 데 경험이 중요하다는 것을 알 수 있다.

그래서 전문가들은 경험을 통해 쌓아온 상황에 맞는 해결책을 디자인 패턴이라는 이름으로 정리했다. 이런 반복되는 패턴들은 특정 설계의 문제점을 해결해주고, 유연하며 재사용 가능한 객체지향 소프트웨어를 만들어 준다.

디자인 패턴: 소프트웨어를 설계할 때 특정 맥락에서 자주 발생하는 고질적인 문제들이 또 발생했을 때 재사용할 수 있는 휼륭한 해결책

건축가이자 디자인 패턴의 아버지, 크리스토퍼 알렉산더(1977/79)는 패턴에 대해 다음과 같이 설명한다.

“Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this. solution a million times over, without ever doing it the same way twice.”

바퀴를 다시 발명하지 마라

Don't reinvent the wheel은 디자인 패턴을 잘 설명하는 격언이다. 이미 만들어져서 잘 되고 있는 것을 처음부터 다시 만들 필요는 없다는 의미다. 즉, 불필요하게 처음부터 다시 시작하지 말라는 것을 강조하는 말이다.

우리는 수십년에 걸쳐 검증된 디자인 패턴을 익히는 것으로 새로운 해결법을 찾는 노력을 들이지 않아도 된다.

GoF의 디자인 패턴

디자인 패턴은 GoF(Gang of Four)Design Patterns: Elements of Reusable Object-Oriented Software에서 소개한 이후 엄청난 인기를 끌었다. 이들이 소개한 패턴은 총 23가지로 목적에 따라 생성 패턴, 구조 패턴, 행동 패턴으로 구분된다.

목적에 따른 분류

생성 패턴

  • 추상 팩토리(Abstract Factory)
    • 구체적인 클래스를 지정하지 않고 객체들의 집합을 생성하는 인터페이스를 제공.
  • 빌더(Builder)
    • 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 다른 결과를 만들 수 있게 함.
  • 팩토리 메서드(Factory Method)
    • 객체를 생성하는 인터페이스는 미리 정의하되 인스턴스를 만들 클래스는 서브클래스에서 결정.
  • 프로토타입(Prototype)
    • 생성할 객체의 종류를 명세하는 데 원형을 정의하고, 이를 복사함으로써 새로운 객체를 생성.
  • 싱글턴(Singleton)
    • 클래스의 인스턴스가 하나임을 보장하고, 이 객체에 접근할 수 있는 전역적인 접촉점을 제공.

구조 패턴

  • 어댑터(Adapter)
    • 클래스의 인터페이스를 사용자가 기대하는 것으로 변환하여 호환성을 해결.
  • 브리지(Bridge)
    • 구현부에서 추상층을 분리하여 각자 독립적으로 변형할 수 있게 함.
  • 합성(Composite)
    • 객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현.
  • 데코레이터(Decorator)
    • 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴.
  • 파사드(Facade)
    • 서브시스템에 있는 인터페이스 집합에 대해서 하나의 통합된 인터페이스를 제공.
  • 플라이웨이트(Flyweight)
    • 크기가 작은 객체가 여럿 있을 때, 공유를 통해 이들을 효율적으로 지원.
  • 프록시(Proxy)
    • 객체로 접근하는 것을 통제하기 위해서 대리자 또는 자리채움자를 제공.

행동 패턴

  • 책임 연쇄(Chain of Responsiblity)
    • 요청을 처리할 수 있는 기회를 하나 이상의 객체에게 부여하여 요청을 주고받는 객체 사이의 결합을 피함.
  • 커맨드(Command)
    • 요청을 객체의 형태로 캡슐화하여 서로 요청이 다른 사용자의 매개변수화, 요청 저장, 로깅, 연산 취소 지원.
  • 해석자(Interpreter)
    • 주어진 언어에 대해, 그 언어의 문법을 위한 표현 수단을 정의. 해당 문서를 해석하는 해석기 정의.
  • 반복자(Iterator)
    • 내부 표현부를 노출하지 않고 어떤 객체 집합에 속한 원소들을 순차 접근.
  • 중재자(Mediator)
    • 한 집합에 속해있는 객체들의 상호작용을 캡슐화하는 객체 정의.
  • 메멘토(Memento)
    • 캡슐화를 위배하지 않은 채로 어떤 객체의 내부 상태를 잡아내고 실체화시켜, 이후에 해당 객체가 그 상태로 되돌아올 수 있도록 함.
  • 옵저버(Observer)
    • 객체들 사이에 일대다 의존 관계를 정의하여, 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그 변화를 통지받고 자동으로 갱신할 수 있게 함.
  • 상태(State)
    • 객체 내부 상태에 따라 스스로 행동을 변경할 수 있게끔 허가.
  • 전략(Strategy)
    • 동일 계열 알고리즘군을 정의하고 각각의 알고리즘을 캡슐화하여 이들을 상호 교환이 가능하도록 만드는 패턴.
  • 템플릿 메서드(Template Method)
    • 객체의 연산에는 알고리즘의 뼈대만을 정의하고 각 단계에서 수행할 구체적 처리를 서브클래스 쪽으로 미룸.
  • 방문자(Visitor)
    • 객체 구조를 이루는 원소에 대해 수행할 연산을 표현하는 패턴.

범위에 따른 분류

  • 클래스 패턴
    • 클래스와 서브클래스 간의 관련성을 다루는 패턴
    • 관련성은 주로 상속을 말함
    • 컴파일 타임에 정적
  • 객체 패턴
    • 객체 관련성을 다룸
    • 런타임에 동적
분류 생성 구조 행동
클래스 팩토리 메서드 적응자(class) 해석자
템플릿 메서드
객체 추상 팩토리
빌더
원형
단일체
적응자(object)
가교
복합체
장식자
퍼사드
플라이급
프록시
책임 연쇄
명령
해석자
중재자
메멘토
감시자
상태
전략
방문자

디자인 패턴 관계도

관계도

참고문헌