DesignPatternSmalltalkCompanion:Head06
6장 결론
여태까지 GoF의 [디자인 패턴] 편에 소개된 23개의 패턴을 모두 토론, 분석, 구현해보았다. 물론 23개의 패턴이 모든 패턴의 세계를 나타내진 못한다. 디자인 패턴 문헌에 소개된 패턴, 잘 알려져 있지만 문헌에서는 소개되지 않은 패턴도 많이 있지만, 기존 애플리케이션에 사용된 패턴에 관심을 많이 기울여야 하는 경우도 있다. 하지만 사실상 디자인 패턴은 훌륭한 디자인 기법의 적용에 불과하다. GoF 패턴들은 객체지향 프로그래밍과 설계의 기본 구성 요소들을 활용하여 더 명확한 모듈식의 해법, 관리와 확장이 수월한 해법을 제공한다. 이 패턴들은 다형성과 지연 바인딩, 모듈화, 책임의 분리, 캡슐화, 정보 은닉과 구현 은닉, 추상화와 필요 시 상속을 통한 행위의 공유를 포함한다. 디자인 패턴은 디자인 패턴과 관련된 저서, 기사, 또는 디자인 패턴 공동체가 생기기 이전부터 전문 설계사들이 줄곧 해온 일의 일부를 담아낸다. Erich Gamma (1991)는 이러한 고수준의 디자인 개념들에ㅡ상호작용하는 객체들에 대한 재귀적이고 성공적인 패턴ㅡ이름을 붙여줘야 하며, 이를 다른 사람들과 공유하고 학습할 수 있도록 문헌으로 기록해야 한다고 생각했다. (한 사람에게 디자인 패턴의 “아버지” 역할을 부여하기란 힘들지만 Erich는 소프트웨어 설계 배경에 "디자인 패턴"이라는 용어를 처음 사용한 사람이다.)
GoF 패턴을 정의하는 핵심이자 매우 중요한 특성으로는 추상화, 위임, 확장성을 들 수 있다. 많은 패턴들이 단일 객체로부터 하나 또는 그 이상의 구분된 헬퍼 객체에게 구체적 행위와 책임을 추상화하는 과정을 수반한다. 예를 들어, 선택할 알고리즘적 전략이 다수일 경우 어떤 것을 호출할 것인지 결정하기 위해 case-like 조건문과 함께 모든 전략들을 주요 애플리케이션 객체 내에 메서드로서 포함하는 일은 하지 않는다. 대신 여러 개의 Strategy 클래스로부터 인스턴스화된 구분된 Strategy 헬퍼 객체로 애플리케이션을 구성하며, 애플리케이션은 헬퍼 객체로 알고리즘의 실행만 위임하도록 한다. 확장성과 관련해서는, 알고리즘을 변경해야 할 경우 우리는 어디서 수정을 해야 하는지를 알고 있다; 새 알고리즘이 추가되었을 경우 새로운 Strategy 클래스를 추가하되 애플리케이션 객체 내 클라이언트 코드는 변경할 필요가 없다.
이와 비슷하게, 상태 기반의 구현 결정은 동적으로 변화하는 State 객체로 위임할 수 없다. 사용자 또는 도메인이 원할 때 상태를 추가하기 위해서는 이러한 구현 세부 사항을 State 객체로 위임하는 애플리케이션 객체에 수정을 거의 하지 않거나 전혀 하지 않아도 된다. Command 패턴은 프로그래머가 실행의 요청과 구현부를 구분하도록 허용한다. 클라이언트는 실행을 요청하는 방법을 알 수 있지만 어떻게 실행되는지에 대한 상세한 내용은 클라이언트에게 숨긴다. Facade나 Bridge 패턴들은 복잡한 구현 세부사항은 구분된 객체로부터 제공되는 추상적 인터페이스 뒤에 숨긴다. Bridge 패턴의 경우, 숨겨진 세부 사항에 다른 하나의 객체를 수반하지만 Facade 패턴의 경우 객체들의 전체 하위시스템이 포함된다. 이와 비슷하게 Adapter 패턴은 호환성이 없는 인터페이스에도 불구하고 객체들이 함께 작동하도록 만드는 것과 관련된다.
Abstract Factory와 Builder 패턴은 동일한 애플리케이션 코드로 서로 다른 유형의 객체들을 구성할 수 있게 해준다. 클라이언트는 구조에 포함시킬 부품의 범주를 조절하지만 부분 인스턴스화는 외부 팩토리 또는 빌더로 위임된다. Client는 자신의 팩토리나 빌더 객체로 추상적 프로토콜을 전달하지만 자신의 헬퍼 객체가 어떤 클래스를 인스턴스화 하는지는 알지 못한다. 따라서 확장성과 관련해, 새 팩토리 또는 빌더를 정의함으로써 새로운 부품군을 전체로서 시스템에 추가할 수는 있으나 클라이언트 코드는 그대로 남는다. 스몰토크에서 Iterator는 반복 오퍼레이션을 위한 추상화를 제공하여 집합체에 서로 다른 유형의 객체들을 포함하더라도 클라이언트가 모든 집합체를 일반적인 방식으로 순회할 수 있도록 한다. 스몰토크의 Iterator는 확장성도 지원하고 있다. 이러한 집합체에 새로운 객체 (클래스) 유형이 추가된다 하더라도 Iterator 클라이언트는 변할 필요가 없다. Composite 패턴은 그 요소들을 위해 동일한 종류의 추상화를 제공한다.
Template Method 패턴은 복잡한 알고리즘을 일반적인 방식으로 정의하여 각 클래스가 자신의 목적에 맞도록 이러한 알고리즘의 일부만 구현할 수 있도록 해준다. 계층구조에 새로운 서브클래스를 추가 시 고유의 버전으로 구현된 알고리즘을 일반적으로 정의된 알고리즘에 참여하도록 허용한다. 다시 말하지만, 클라이언트 코드는 변경할 필요가 없다; 알고리즘의 실행을 초기화하기 위해 동일한 메시지를 전송할 뿐이다.
Visitor 패턴은 객체 또는 객체의 집합에 새로운 오퍼레이션을 쉽게 추가하도록 해준다ㅡ사실상 서로 매우 다른 컴포넌트 객체를 포함하는 집합에 추가를 허용한다. 새로운 오퍼레이션의 추가 시 Visitor 패턴에 따른 인터페이스의 사용을 통해 새 클래스가 추가되지만 실행되는 클래스는 변경되지 않는다.
다수의 객체에 책임을 위임함으로써 책임을 모듈화할 경우 우리는 각 객체 클래스의 설계를 깔끔하게 정리한다. 그리고 애플리케이션 객체로부터 주요 책임을 추상화하여 지식과 세부 내용을 헬퍼 객체로 분배할 때는 애플리케이션은 자신의 전체적 기능성을 가져오기 위해서는 그러한 헬퍼 객체들에게 전송할 추상적 메시지만 알면 된다.
문제를 작은 크기의 모듈식 하위문제로 분해할 때 우리는 추가 헬퍼 객체 또는 헬퍼 패턴과 같이 다른 패턴에게 부차적 역할을 하는 패턴으로 하위컴포넌트를 표현할 수 있다. 때로는 패턴들이 동일한 업무에서 경쟁상대가 되기도 한다; Abstract Factory와 Builder 패턴은 기본적인 작업에서 서로 경쟁한다. 하지만 서로 결합하여 문제를 해결하는 패턴들도 있다: UIBuilder 객체는 자신의 클라이언트와 Builder 패턴에 참여하지만 Abstract Factory 패턴을 불러와 자신의 일을 수행하도록 만들기도 한다. UILookPolicy로 UIBuilder를 구성함으로써, 여러 가능한 플랫폼 특정적 위젯군 중 하나로부터 위젯을 생성하는 Builder의 행위를 다양화시킬 수 있다.
이러한 패턴들을 이용 시 관리와 확장이 가능한 프로그램이 생긴다. 초기 디자인은 한정된 정보에 의존한다는 점을 항상 명심해야 한다. 설계 당시에 얼마나 많은 지식을 갖고 있든 애플리케이션의 구현이 끝날 무렵이면 사용자 또는 도메인은 더 많은 확장과 향상을 요구할 것이다. 이것이 바로 다수의 패턴에서 확장성을 목표로 둔 이유이다.
패턴을 똑똑하게 사용하는 것은 추후 관리와 향상에 나타나는 어려움을 개선하는 하나의 도구이다. 예를 들어, 많은 패턴들이 각 메서드 또는 클래스로 변경 내용을 분리시키는 것을 허용한다. 이에 따라 새로운 클래스 추가 시 다수의 위치에서 변경을 일으키기 않고 클래스의 추가만 실행하는 관리 형태를 허용한다 (Pascoe, 1986). 사실상 GoF의 모든 패턴들은, Cox (1986)가 “유연성 있는” 소프트웨어ㅡ장애가 되기보다는 변경, 재사용, 향상을 지원하고 용이하게 하는 소프트웨어ㅡ라는 이름의 개념을 발전시키는데 일조했다고 볼 수 있다.
끝으로, 소프트웨어 개발자가 그들의 작업에 반영적일수록 디자인 패턴을 적용할 수 있는 기회는 늘어난다. 수 많은 디자인 도전과제가 (기회) 발생하고 나서야 우리가 패턴을 구현하고 있음을 깨닫는다. 넘쳐나는 디자인 방법론과 패턴에도 불구하고 설계 시 모든 문제를 이해하고 예측하는 방법은 없다; “디자이너”란 “구현자(implementers)”에게 디자인을 단순히 넘겨주는 것이라는 개념은 잘못된 것임이 밝혀졌다. 디자인은 구현 이전 뿐 아니라 구현 도중, 그리고 관리 시에 발생한다. 소프트웨어 개발자는 주기적으로 작업에서 한 발 물러나 최근 디자인이 적절한지, 뭔가 “더” 필요하진 않은지, 새로운 디자인의 기회나 필요성이 발생했는지, 디자인 패턴에 관해 인식된 과정이나 요구사항은 없는지 되돌아보는 것이 중요하다.
Donald Schöen은 (1987) 전문 실습에서의 반영에 관해 광범위하게 연구를 진행하여 글을 썼다. 그는 까다로운 문제를 해결하기 위한 성공적인 해법을 얻으려면 반영과 실행의 조화가 요구되며, 작업을 진행하면서 주기적으로 진행 중인 작업에서 한 발 물러나 이를 분석할 필요가 있다고 주장했다. 그는 실행에 반영하는 의지와 능력을 고차 기술로 간주한다. 따라서 구현 도중에 재설계를 두려워하지 않아도 된다. 본래 디자인에 없던 기능을 추가할 수 있는 기회를 찾을 수 있을 것이다. 디자인 패턴을 작업했던 사람이라면 누구나 한번쯤 이 경험을 해봤을 것이다.