디자인 패턴이란?

 

본 글은 자바 객체지향과 디자인패턴를 읽고 개인적으로 학습한 내용을 복습하기 위해 작성된 글로 내용상 오류가 있을 수 있습니다. 오류가 있다면 지적 부탁드리겠습니다.

1. 디자인 패턴이란?

디자인 패턴이란 소프트웨어를 설계할 때 특정 맥막에서 자주 발생하는 고질적인 문제들이 또 발생했을 때 재사용할 수 있는 해결책이라고 생각하면 된다. 디자인 패턴을 사용해 문제를 해결하려는 시도는 소프트웨어 분야가 처음이 아니다. 약 20여년 전 건축가 크리스토퍼 알렉산더가 건축분야에서 시도했었다. 알렉산더는 패턴을 다음과 같이 정의했다.

각 패턴은 우리 주변에서 자주 반복해서 발생하는 문제와 그 문제를 해결하는 핵심을 기술해 동일한 일을 두번 다시 하지 않고 해결할 수 있도록 한다.

그렇다면 패턴이란 무엇일까?

패턴은 비슷하거나 동일한 양식 또는 유형들이 반복되어 나타난다는 의미이며, 문제와 해결책도 동일한 유형이나 양식을 통해 쉽게 찾을 수 있다.

이제 디자인 패턴의 구조에 대해 알아보자. 디자인 패턴의 구조는 다음과 같이 3개의 필수적인 요소로 구성된다.

  • 콘텍스트 : 문제가 발생하는 여러가지 상황을 기술한다. 즉, 패턴이 적용될 수 있는 상황을 나타낸다. 경우에 따라 패턴이 유용하지 못한 상황을 나타내기도 한다.
  • 문제 : 패턴이 적용되어 해결될 필요가 있는 여러 디자인 이슈들을 기술한다. 이때 여러 제약 사항과 영향력도 문제해결을 위해 고려해야 한다.
  • 해결 : 문제를 해결하도록 설계를 구성하는 요소들과 그 요소들 사이의 관계, 책임, 협력 관계를 기술한다. 해결은 반드시 구체적인 구현 방법이나 언어에 의존적이지 않으며, 다양한 상화에 적용할 수 있는 일종의 템플릿이다.

2. GoF 디자인 패턴

에리히 감마(Erich Gamma), 리차드 헬름(Richard Helm), 랄프 존슨(Ralph Johnson), 존 블리시디스(John Vlissides)는 소프트웨어 개발 영역에서 디자인 패턴을 구체화하고, 체계화한 GoF(Gang of Four)라 불리는 사람들이다. 이 사람들은 디자인 패턴 책에서 디자인 패턴을 23가지로 정리하고 생성, 구조, 행위의 3가지로 분류했다. 아래는 GoF 디자인 패턴을 분류한 것이다.

  • 생성패턴 : 객체 생성에 관련된 패턴으로 객체의 생성과 조합을 캡슐화해 특정 객체가 생성되거나 변경되어도 프로그램에 영향을 크게 받지 않도록 유연성을 제공한다.
    • 추상 팩토리(Abstract Factory) : 구체적인 클래스에 의존하지 않고 서로 연관되거나 의존적인 객체들의 조합을 만드는 인터페이스를 제공하는 패턴
    • 빌더(Builder)
    • 팩토리 메서드(Factory Method) : 객체 생성 처리를 서브 클래스로 분리해 처리하도록 캡슐화하는 패턴
    • 프로토 타입(Prototype)
    • 싱글턴(Singleton) : 전역 변수를 사용하지 않고, 객체를 하나만 생성하도록 하며, 생성된 객체를 어디서든 참조할 수 있도록 하는 패턴
  • 구조패턴 : 클래스나 객체를 조합해 더 큰 구조로 만드는 패턴이다. 예를 들어 서로 다른 인터페이스를 2개 지닌 객체를 묶어 단일 인터페이스를 제공하거나 객체들을 서로 묶어 새로운 기능을 제공하는 패턴이다.
    • 어댑터(Adaptor)
    • 브리지(Bridge)
    • 컴퍼지트(Composite) : 여러 개의 객체들로 구성된 복합 객체와 단일 객체를 클라이언트에서 구별 없이 다루게 해주는 패턴
    • 데커레이터(Decorator) : 객체의 결합을 통해 기능을 동적으로 유연하게 확장할 수 있게 해주는 패턴
    • 퍼사드(facade)
    • 플라이웨이트(Flyweight)
    • 프록시(Proxy)
  • 행위패턴 : 객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴이다. 가령 한 객체가 혼자 수행할 수 없는 작업을 여러 개의 객체로 어떻게 분배하는지, 또 그렇게 하면서도 객체 사이의 결합도를 최소화하는 것에 중점을 둔다.
    • 책임 연쇄(Chain of Responsibility)
    • 커맨드(Command) : 실행될 기능을 캡슐화함으로써 주어진 여러기능을 실행할 수 있는 재사용성이 높은 클래스를 설계하는 패턴
    • 언터프리터(Interpreter)
    • 이터레이터(Iterator)
    • 미디에이터(Mediator)
    • 옵저버(Observer) : 한 객체의 상태 변화에 따라 다른 객체의 상태도 연동되도록 일대다 객체 의존 관계를 구성하는 패턴
    • 스테이트(State) : 객체의 상태에 따라 객체의 행위 내용을 변경해주는 패턴
    • 스트래티지(Strategy) : 행위를 클래스로 캡슐화해 동적으로 행위를 자유롭게 바꿀 수 있게 해주는 패턴
    • 템플릿 메서드(Template Method) : 어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴
    • 비지터(Visitor)

3. UML과 디자인 패턴

UML 2.0에서 디자인 패턴을 표현하는 도구로 컬라보레이션이 있다. 컬라보레이션을 통해 디자인 패턴을 정확하게 표현하려면 구조적인 면과 행위적인 면을 모두 표현할 필요가 있다.

  • 구조적인 면 : 어떤 요소들이 주어진 목적을 달성하기 위해 협력하는지는 나타낸다.
  • 행위적인 면 : 협력을 위한 요소들의 상호작용을 나타낸다.

3.1 컬라보레이션

혼자서는 모든일을 다 처리할 수 없다. 아주 쉬운 일이라도 대부분은 다른 사람의 도움이 필요하다. 객체지향 시스템에서도 마찬가지로 어떤 주어진 목적을 달성하려고 여러 객체가 각자 자신이 맡은 역할에 따라 일을 수행하며 서로 협력한다.

여기서 주목해야할 점은 객체와 역할 사이의 관계이다. 객체는 역할이 아니므로 한 객체가 여려 역할을 수행할 수 있다. 예를 들어 철수는 집에서 아버지 역할을 수행하며 회사에서는 프로그래머 역할을 수행한다. 또한 축구 동호회에서는 총무역할을 수행한다. 따라서 어떤 일을 수행할 때 객체는 각자 주어진 상황에서 주어진 역할에 따른 책임을 수행한다. 집에서 아버지 역할을 수행할 때에는 회사 업무를 수행할 때 필요한 기능을 요구하지 않는다.

디자인 패턴 역시 목적을 달성하기 위한 역할들의 상호 협동 작업으로 간주할 수 있다. 그리고 UML에서는 객체들이 특정 상황에서 수행하는 역할의 상호작용을 컬레보레이션이라는 요소로 작성한다.

UML에서 컬레보레이션은 점선으로 된 타원 기호를 사용하며, 타원 내부에 협력을 필요로 하는 역할들과 그들 사이의 연결관계를 표현한다. 아래의 그림은 담보 대출 관계를 보여주는 컬레보레이션으로 대출자, 대출인, 담보라는 역할이 필요하고 그들 사이의 협력이 요구되므로 이 역할들을 커넥터로 연결한다.

컬레보레이션

컬레이보레이션은 객체가 수행하는 역할들의 협력을 표현해준다.

컬레보레이션은 역할들의 상호작용을 추상화한 것으로 특별한 상황에 적용하면 많은 시스템 개발에 재사용할 수 있다. 컬라보레이션 어커런스(Collaboration Occurence)는 더 구체적인 상황에서의 컬레보레이션 적용을 표현해준다.

켈레보레이션 어커런스

위의 그림은 담보 대출 컬레보레이션을 은행에서 집을 담보로 대출하는 경우를 적용한 것이다. 대출자 역할은 은행, 담보 역할은 집, 대출인은 사람이다. 이 경우에 은행집담보대출은 담보 대출의 예이고, 컬레보레이션 어커런스이다.

즉, 컬레보레이션 어커런스는 컬레보레이션에 참가하는 응용 클래스가 컬레보레이션에 수행하는 역할을 나타낸다.

3.2 순차 다이어그램

순차 다이어그램은 UML 2.0에서 객체들의 상호작용을 나타내는 다이어그램 중 하나다. 순차 다이어그램은 객체들 사이의 메시지 송신과 그들의 순서를 나타낸다.

아래의 그림은 순차 다이어그램에서 객체를 표현하는 3가지 방법을 나타낸 것이다.

객체의 3가지 표현

  • 객체는 가장 윗부분에 표현되고, 왼쪽에서 오른쪽으로 객체들을 나열한다.
  • 객체 다이어그램에서 객체를 표현하는 표기와 동일하게 “객체 이름 : 클래스 이름” 형식을 이용해 표기하며, 이 중 어느 한쪽을 생략해 표기할 수 있다.
  • 객체 아래에는 점선이 뻗어 나가는데 이것을 “생명선”이라 하고 해당 객체가 존재함을 보여준다.
  • 생명선을 따라서 좁고 긴 사각형이 나올 수 있다. 이 상자를 “활성구간”이라 한다. 이는 실제로 객체가 연산을 실행하는 상태임을 보여준다. 활성 구간의 크기는 실행시간을 고려하여 적당히 설정한다.

아래의 그림은 여러가지 형태의 메시지 표현을 나타낸 것으로 객체 사이의 메시지는 화살표로 표시한다.

여러가지 형태의 메시지 표현

  • 화살표의 시작 부분은 메시지를 송신하는 객체를 나타내고 화살표의 끝 부분은 메시지의 수신 객체를 나타낸다.
  • 순차 다이어그램에서 종종 머리부분이 채워지지 않고 열려있는 화살표를 볼 수 있는데 이 것은 비동기 메시지를 나타낸다.
  • 비동기 메시지는 메시지를 송신 후 메시지 실행이 끝나기를 기다리지 않고 다음 작업을 바로 수행할 수 있다.
  • 머리 부분이 채워진 메시지는 동기메시지라 하며 메시지의 실행을 요청하는 객체가 메시지의 실행이 종료될 때까지 다음 작업을 수행할 수 없다.

위의 순차 다이어그램에서는 약간 특이한 메시지를 볼 수 있다.

  • Y 클래스의 한 인스턴스에서 Y 클래스의 o 인스턴스에게 보내는 메시지에 <<create>>라는 스테레오 타입을 붙인 메시지를 송신하고 있는데 이 것은 객체를 생성하는 메시지를 표현한다.
  • 객체를 소멸시키고자 할 때는 <<destroy>>라는 스테레오 타입 메시지를 보내고, 소멸되는 객체의 생명성 끝에 X를 넣는다.

그리고 메시지를 표현할 때는 [시퀀스 번호][가드]:반환값:=메시지 이름([인자 리스트])와 같은 형식을 따른다.

  • 메시지 이름을 제외하고 나머지는 모두 생략이 가능하다.
  • 시퀀스 번호는 순차 다이어그램에서 기술할 필요가 없다. 그 이유는 생명선에 따라 시간이 위에서 아래로 지나가므로 생명선 아래쪽에 있는 메시지일수록 나중에 송신한 것이기 때문이다.
  • 가드는 메시지가 송신되는데 만족해야하는 조건이다.
  • 점선 화살표는 응답메시지를 표현한다. 응답메시지는 메시지가 종료 되었음을 표현하며 반드시 표현해야하는 것은 아니다.

UML 2.0에서는 모든 다이어그램에 다이어그램의 경계, 타입, 이름을 포함한 레이블의 장소를 제공하는 프레임을 제공한다.

  • 프레임은 다이어 그램을 에워싸는 박스로 표시하며 박스 안 왼쪽 모서리에 다이어그램 타입과 이름을 넣을 수 있다.
  • 순차 다이어그램의 타입에 해당하는 키워드는 sd이다.
  • 유즈케이스 다이어그램은 uc, 액티비티 다이어그램은 act 등의 키워드로 표시한다.

아래의 순차 다이어그램은 도서관에서 도서를 대여하는 과정을 순차 다이어그램 프레임으로 나타낸 것이다.

프레임을 사용한 순차 다이어그램

프레임을 사용하면서 다이어그램 외부에서 특정 다이어그램을 참조하는 것이 쉬워졌다. 특히 순차 다이어그램에서 객체 사이의 상호작용이 이루어지는 부분의 일부분이 다른 곳에서 재사용되는 경우나 순차 다이어그램으로 매우 복잡한 상호작용을 모델링하는 경우에는 하나의 순차 다이어그램에서 모든 상호작용을 표현하지 않고 분리해 작성한 후 이를 참조할 수 있게 하는 것이 좋다. 이 때 순차 다이어그랜에서는 ref키워드를 사용해 다른 순차 다이어그램을 참조한다.

객체 사이의 상호작용을 나타내는 논리가 어떤 상호 작용의 반복이거나 여러 개의 선택적인 상호 작용이 관련되어 있는 경우라면 매우 복잡할 수 있다. 순차 다이어그램은 기본적으로 하나의 시나리오에 관한 객체 사이의 상호작용을 보여주는데 사용되어야 한다. 알고리즘과 같이 여러 가능한 시나리오들을 한번에 보여주는데 사용되면 너무나 많은 객체와 메시지로 가득차서 순차 다이어그램을 이해하기 어렵기 때문이다.

아래의 그림은 위의 그림에서 도서 대여 시나리오에 올바르지 않은 비밀 번호를 입력한 경우를 고려해 확장한 순차 다이어그램으로 이 경우 alt키워드를 사용해 상호작용을 조건에 따라 선택적으로 수행할 수 있게 한다.

alt 키워드

선택 연산자 alt외에도 반복적인 상호작용을 나타내는 loop키워드도 있다. 아래의 그림은 올바르지 않은 비밀번호를 입력했을 때 3회까지 비밀번호를 입력하도록 도서 대여 시나리오를 변경한 것이다.

loop 키워드

그 외에 유용한 상호작용 연산으로는 아래와 같은 것들이 있다.

  • opt : 특정 조건에서만 상호작용을 선택적으로 수행한다.
  • par : 동시에 실행되는 상호작용을 수행한다.
  • break : C 프로그래밍 언어의 break키워드와 같이 특정 상호작용 그룹을 빠져나갈 때 사용한다. 하지만 break 연산자 부분은 수행한다.

3.3 순차 다이어그램과 클래스 다이어그램의 관계

순차 다이어그램과 클래스 다이어그램의 관계에 대해 알아보자.

  • 순차 다이어그램 : 객체 사이의 메시지 흐름과 순서를 알려주는 행위 측면에 중점을 두는 모델
  • 클래스 다이어그램 : 시스템 구조적인 측면에 중점을 두는 모델

순차 다이어그램과 클래스 다이어그램은 서로 밀접한 관계가 있으며, 이들을 이용해 시스템을 모델리할 때는 서로 정합이 이루어져야 한다.