좋은글

디자인패턴

아르비스 2021. 2. 20. 20:20

Design Pattern

 

 

디자인패턴은 앞서 설명한 바와 같이 목적에 따라 '생성', '구조', '행위' 패턴으로 나눌 수 있고,

범위에 따라 클래스를 대상으로 하느냐, 객체를 대상으로 하느냐, 로 나눌 수 있다.

 

1. 생성패턴

 

[생성패턴-클래스]
Factory Method : 서브클래스에 인스턴스 결정 및 책임을 위임


[생성패턴-객체]
Abstract Method : 생성군을 캡슐화, 한군데 모아놓고 팩토리 1, 2중에서 다이나믹하게 선택
Builder : 생산단계를 캡슐화, 다른 표현 방식을 가지고도 동일한 구축 공정을 이용
Prototype : 복사해서 새로운 개체를 생성, 시간/자원효율성↑
Singleton : 유일하게 존재하는 하나의 인스턴스 보장

 

2. 구조패턴

 

[구조패턴-클래스/객체]
Adapter : 호환성이 없는 인터페이스 때문에 함께 사용할 수 없는 클래스를 개조하여 함께 작동

 

[구조패턴-객체]
Bridge : 추상부/구현부를 분리, 인터페이스와 구현의 결합도 약화
Composite : 클라이언트에서 개별객체와 복합객체를 동일하게 사용, 트리구조, 재귀호출
Decorator : 기능을 확장하기 위해 유연성을 제공, 소스를 변경하지 않고서도 실행 가능
Façade : 하나의 인터페이스 제공, 인터페이스 단순화, 느슨한 결합
Flyweight : 대량의 작은 객체들을 효과적으로 사용하기 위해 공유, 불필요한 New 방지, Pool
Proxy : 바빠서 그 일을 할 수 없는 본인 객체 대신 그 일을 처리, 원격/가상/보호용/스마트

 

3. 행위패턴

 

[행위패턴-클래스]
Interpreter : 언어 규칙을 나타내는 클래스를 이용하여 언어 표현, 문법 변경/확장 용이
Template : 알고리즘 골격 구조 정의, 구체적인 단계는 서브클래스에서 정의, Hook Operation

 

[행위패턴-객체]
Chain of Responsibility : 요청을 처리하는 객체들끼리 연결고리를 만들어 요청을 내부적으로 전달
Command : 요청 자체를 객체화(캡슐화) 해서 파라미터로 넘김, 실행취소/로그남김/명령어 조합
Iterator : 복합객체의 내부 표현은 보여주지 않고 순회하여 원하는 데이터를 찾아가도록 함
Mediator : 객체들간의 상호작용을 캡슐화, 통제 집중화
Memento : 상태값을 미리 기록/저장해 두었다가 복구, 이전값 저장으로 인한 오버헤드 발생
Observer : 하나의 오브젝트 상태가 변할 때 의존자들에게 알리고, 필요 시 자동 업데이트
State : 객체 자체의 내부 상태에 따라 행위를 변경
Strategy : 다양한 알고리즘 각각을 하나의 클래스로 캡슐화하여 알고리즘의 대체가 가능
Visitor : 방문을 통해 수행할 수 있도록 오퍼레이션을 별도의 클래스에 새롭게 정의, 캡슐화 위배

 


1. 추상팩토리 패턴(Abstract Factory Pattern)

 

Abstract Factory 패턴은 구체적인 클래스를 정의하지 않고도 서로 관련성이 있거나 독립적인 여러 객체의 군을 생성하기 위한 인터페이스를 제공하는 패턴을 말한다.

 

생성군을 모아놓고, 팩토리 1, 2…n 중에서 다이나믹하게 선택하여 객체를 생성할 수 있는데,

따라서 개별 클래스에 포함되지 않으므로 변경이 용이하다는 장점이 있다.

 

Abstract Factory 패턴의 클래스 다이어그램은 다음과 같다.

 

Abstract Factory 패턴은 여러 운영체제에서 동일하게 실행이 가능한 컴파일러를 개발하여야 하는  경우에 사용할 수 있으며, 다이어그램에서 보는 바와 같이 컴파일러 팩토리를 통해 해당 환경에서 동작하는 클래스(HP Scanner, HP Paser..)의 객체만을 생성할 수 있습니다.

 


2. 빌더 패턴(Builder Pattern)

 

Builder 패턴은 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴이다.

 

즉, 복잡한 Object의 생산 단계를 캡슐화하여 다른 표현 방식을 가지고도 동일한 구축 공정을 이용할 수 있게 하는데요. Builder 패턴의 다이어그램은 다음과 같다.

 


Builder 패턴은 자동 번역 소프트웨어 개발과 같은 경우에 적용할 수 있는데, 한국어로 된 매뉴얼을 입력하면 개별 문장 단위로 번역 후, 부분부분의 생성을 통해 전체(영어, 중국어..) 매뉴얼을 생성할 수 있게 해주며, 이 때의 각 참여자의 역할은 다음과 같다.

 

- BuildPart : 각각의 분야(Bulider는 buildPart를 개수만큼 호출)
- getResult : 최종 결과값(Director 쪽에 호출)
- Director : 조립해서 인스턴스를 생성

 

관련 패턴으로는 Composite 패턴 등이 있다.


3. 팩토리메소드 패턴(Factory Method Pattern)

 

Factory Method 패턴은 객체를 생성하는 인터페이스는 미리 정의하되, 어떤 클래스의 인스턴스를 생성할 지에 대한 결정은 서브클래스에서 이루어지도록 하는 패턴을 말한다.

 

쉽게 말해, 서브클래스에 인스턴스 결정 및 책임을 위임하는 패턴으로, Abstract Factory와 Template Method 패턴에 사용될 수 있으며, 서브클래스에 대한 Hook(객체별로 서로 다른 버전을 제공하는 기능) 메소드를 제공하는 패턴이다.

 

 

위 다이어그램에서 보는 바와 같이 직접 객체 생성자를 호출하여 생성하는 것이 아니라, 대행함수를 통해 객체를 생성하는데, 이 때의 대행함수를 Factory Method라고 하는 것이다.

 

윈도우에서 확장자에 따라 적절한 Application이 실행되게 하고자 할 때 사용할 수 있고, 운영체제가 호출하는 인터페이스는 동일하게 유지하면서 각 응용 프로그램마다 고유한 객체를 생성할 수 있게 해준다는 특징을 가지고 있다.


4. 프로토타입 패턴(Prototype Pattern)

 

Prototype 패턴은 클래스로부터 인스턴스를 만드는 것이 아니라, 복사해서 새로운 객체(인스턴스)를 생성하는 패턴을 말한다.

 

기존 인스턴스를 복사하여 쉽게 새로운 인스턴스를 만들 수 있어, 시간/자원효율성을 높이고 서브클래스의 수를 줄일 수 있다는 특징을 가지고 있으며, 여기서 Prototype 클래스는 자신을 복제하는데 필요한 인터페이스를 정의하는 역할을 한다.

 

 

Prototype 패턴은 런타임에 새로운 제품을 삽입하고, 삭제를 가능하게 해준다.
그래픽 편집기와 같은 응용 프로그램의 설계에 사용될 수 있으며, 각각의 그래픽 요소들이 추가될 때, 가장 쉽고 유연한 방식인 복제를 통해 객체를 생성하게 된다.


5. 싱글톤 패턴(Singleton Pattern)

 

Singleton 패턴은 클래스가 오직 한 개의 인스턴스만을 가지도록 하고, 어디에서든지 접근할 수 있도록 하는 패턴이다.


유일한 인스턴스 Static uniqueInstance와 Static Instance()를 정의하여, 유일하게 존재하는 인스턴스로의 접근을 통제 가능하게 하는데,  전략게임에서와 같이 생성 가능한 유닛 수를 최대 1개로 제한하고자 하는 경우 사용할 수 있다.

 

 

Abstract Factory, Facade 패턴 등과 연관 관계를 가지고 있다.


6. 적응자 패턴(Adapter Pattern)

 

Adapter 패턴은 호환되지 않는 인터페이스 때문에 함께 사용할 수 없는 클래스의 인터페이스를 변환하여 함께 작동할 수 있게 해주는 패턴이다.

 

이미 만들어진 것을 재사용 하려 하지만 라이브러리를 수정할 수 없을 경우에 사용할 수 있고, 클래스를 Adapter로 만들거나, 객체를 Adapter로 만들어 구현할 수 있다.

클래스 Adapter의 경우에는 다중상속을 이용하고, 객체 Adapter의 경우에는 객체합성을 이용하게 된다.

 


그래픽 편집기에서 TextView 라이브러리를 사용하고자 하는 경우를 예로 들어 설명하면,

 

위의 다이어그램에서 보는 바와 같이
① 다중상속의 경우에는 TextShape 클래스를 Shape TextView로부터 동시에 상속받아 정의하는 구조이고,
② 객체합성의 경우에는 TextShape의 인스턴스가 호출되면 그 내부에서 TextView의 인터페이스를 호출하는 구조이다.(TextView 클래스 참조를 통한 TextShape 클래스 구현)


7. 가교 패턴(Bridge Pattern)

 

Bridge 패턴은 구현부(Implement)에서 추상층(Abstration)를 분리하여, 각자 독립적으로 변형할 수 있게 하는 패턴이다.

 

Bridge 패턴의 클래스 다이어그램은 다음과 같다.

 

 

구현부가 어떤식으로 구현했는지는 은닉함으로써, 종속성을 없애고 결합도를 낮추는 패턴이다.


8. 컴포지트 패턴(Composite Pattern)

 

Composite 패턴은 객체들의 관계를 트리구조로 구성하여 부분-전체 계층을 표현하는 패턴을 말한다.

 

클라이언트에서 개별 객체와 복합 객체를 동일하게 다룰 수 있도록 하고, ERD와 마찬가지로 재귀적인 구조를 만든다. 또한 자기 스스로 Association 관계를 가지는 구조로 간결히 표현도 가능한데, 아래 보이는 Leaf 클래스는 더 이상 하위개체를 가지지 않는 개체만 따로 모은 클래스를 의미한다.

 

 

Composite 패턴은 복잡하게 조합된 도형들을 반복 사용하고자 할 경우, 어떤 객체의 구성원들이 동적으로 연결될 때 기본 객체와 구성 객체를 구분없이 사용할 수 있다.


9. 장식자 패턴(Decorator Pattern) 

 

Decorator 패턴은 서브클래스에 유연성을 제공하여 소스를 변경하지 않고서도 부가적인 기능이 확장되어 실행될 수 있도록 하는 패턴이다.


상속을 하지 않고 동적으로 새로운 서비스를 추가하기 위하여 사용되며, 메인 기능은 ConcreteComponent에서 구현하고, 부가적 기능은 Decorator에서 확장할 수 있도록 하고 있다.

 

 

 

Association 관계는 장식품을 추가할 때 원래 기능을 포함에서 장식을 추가해야 하기 때문에 필수적이고, 그래픽 편집기에서 사진이 Component 라면 액자의 색깔, 크기, 고급성은 Decorator로 하여 장식을 해나갈 수 있습니다.


10. 퍼사드 패턴(Facade Pattern)

 

Facade 패턴은 서브시스템을 호출하는 것이 복잡하기 때문에 단순화된 하나의 인터페이스로 제공하기 위한 패턴이다.

 

Facade를 통해서만 접근 가능하기 때문에 결합도를 낮출 수 있으며, Mediator 패턴과 혼동될 수 있지만, Facade 패턴은 기능만을 모아 놓은 구조이고, Mediator 패턴은 복잡한 기능을 제어하기 위한 행위(상호작용) 패턴이다.

 

 

Facade 패턴은 검색 요청을 위해 복잡한 과정을 거쳐야 하는 경우에 여러 개의 클래스들이 밀접한 관계를 가지고 대표 클래스를 통해 원하는 기능을 제공할 수 있도록 해준다.


11. 플라이급 패턴(Flyweight Pattern)

 

Flyweight 패턴은 대규모의 작은 객체들을 효과적으로 사용하기 위해 본질적 상태(공유 가능)와 부가적 상태(공유 불가능)를 구분하여 불필요한 New를 하지 않도록 하는 패턴을 말한다.

 

작고 대량의 객체들을 공유(경영에서의 MRO 사용과 비슷)하는 개념으로, 이미 Pool 에 있다면 있는 것을 사용하고, 없는 경우에는 집어 넣어 사용하게 된다.

 


플라이급 패턴을 이용하면 서식이 있는 Text에서 문서 작성 시, 폰트/포인트를 pool에 넣고 복제하여 사용하다가 바뀔 때만 등록해서 사용(폰트/포인트가 계속 바뀌지는 않음)하도록 구현할 수 있다.


12. 프록시 패턴(Proxy Pattern)

 

Proxy 패턴은 어떤 다른 객체로 접근하는 것을 통제하기 위해서 그 객체의 대리자 또는 자리채움자를 제공하는 패턴을 말한다.

 

2가지 경우에 사용하게 되는데,
 바빠서 그 일을 할 수 없는 본인 객체 대신 대리인 객체가 어느 정도 그 일을 처리해 줄 필요가 있거나,
② 다른 객체에 접근하기 위해 본인 객체는 은닉하고 대리인 객체를 두고자 하는 경우에 사용한다.

 

 

프록시 패턴은 한전된 자원을 가지고 서비스 요청이 폭주하는 시간에도 안정되고 빠르게 유지하기 위한 웹툰 서비스 등에 이용하며, 아래의 4가지 유형으로 구분할 수 있다.

 

- 원격 프락시 : 원격에 있음에도 불구하고 바로 옆에 있는 것처럼 호출
- 가상 프락시 : 인스턴스가 필요한 시점에만 생성
- 보호용 프락시 : RealSubject 기능에 대해 액세스 제한을 설정
- 스마트 프락시 : 객체에 대한 단순한 접근 외에 부가적인 작업 수행

 


13. 책임연쇄 패턴(Chain of Responsibility)

 

Chain of Responsibility 패턴은 메시지를 보내는 객체와 이를 받아 처리하는 객체들 간의 결합도를 없애기 위한 의도에서 나온 것으로, 메시지를 처리하는 객체들끼리 연결고리를 만들어, 실제 요청을 처리하는 객체를 만날 때까지 계속해서 요청을 내부적으로 전달하는 패턴을 말한다.

 

자기가 받은 요청을 검사하여 직접 처리하거나 사슬에 들어있는 다른 객체에 넘기게 되며,

이 때, 요청을 하는 객체는 다른 객체에 찾아가는 것이 아니라 심플하게 요청만 하게 된다.

 

 

이 책임연쇄 패턴은 도움말 기능에서 사용할 수 있는데, 사용자의 상황을 자동 판별하여 도움말을 제공하여야 할 때, 수행 가능한 객체를 만날 때까지 체인을 통해 계속 전파되어, 처리하게 된다.


14. 명령 패턴(Command)

 

Command 패턴은 요청 자체를 객체화(캡슐화)하여 클라이언트에 파라미터로 넘길 수 있게 하기 위한 패턴이다.

 

실행 취소에 필요한 상태 저장, 로그 기록(복구 가능), 명령어 조합을 가능하게 하고, Composite 패턴을 이용하여 여러 명령어의 구성이 가능하다는 특징이 있다.

 

 

-Command : 오퍼레이션 수행에 필요한 인터페이스 선언
-invoker : 명령어를 커맨드 클래스에 요청
-Receiver : 요청을 처리하는데 관련된 오퍼레이션 수행방법을 알고 있음


CGI 게시판 환경에서 서비스 항목이 추가되는 경우를 활용 사례로 들 수 있다.
서비스 항목이 추가된다는 것은 요청의 종류가 추가되고, 처리해야 할 작업이 추가된다는 것을 의미하는데, 이 때, 객체의 저장/관리/중계를 통해 기존의 개발해 두었던 부분들을 수정하지 않고, 새로운 요청에 대한 추가를 쉽게 구현할 수 있다.

 


15. 인터프리터 패턴(Interpreter)

 

Interpreter 패턴은 간단한 언어의 문법을 정의, 문장을 구성, 문장을 해석하는 방법을 제시하는 패턴이다.
 

언어에 속하는 각 규칙을 나타내는 클래스를 이용하여 언어를 표현하고, 문법의 변경 및 확장이 쉬워, 구현이 용이하는 특징이 있다.

 

 

 

-Terminal Expression : 문법에 정의한 터미널 기호와 관련된 해석방법 구현
-Nonterminal Expression : 문법의 오른편에 나타난 모든 기호에 대해 정의
-Context : 번역기에 대한 포괄적 정보

 

문서파일에서 특정 문자열 패턴이 존재하는지 검사하고자 할 경우 사용할 수 있는데, 간단한 문법에 대해 문법 자체를 클래스로 정의하여 별도의 자료구조 없이 효율적으로 수행할 수 있다.


16. 이터레이터 패턴(Iterator)

 

Iterator 패턴은 내부 표현부를 노출하지 않고 어떤 객체 집합에 속한 원소들을 순차적으로 접근할 수 있도록 하는 패턴을 말한다. 즉, 복합객체의 내부 표현은 보여주지 않고 순회(비공개순회)하여, 원하는 데이터를 찾아갈 수 있게 해준다는 특징을 가지고 있다.

 

 

-Iterator : 순회에 필요한 인터페이스 제공
-Concrete Iterator : 구현
-Aggregate : Iterator 자체를 생성
-Concrete Aggregate : 실제로 인스턴스를 반환하도록 구현

 

트리자료구조에서 자료형의 객체를 순차적으로 접근하려 할 경우 사용할 수 있다.


17. 중재자 패턴(Mediator)

 

Mediator 패턴은 객체들간의 상호작용을 캡슐화하여 다른 독립된 객체가 loosely Coupled 하게 관리 할 수 있게 하는 패턴을 말한다. 이를 통해 서브클래스들이 많아지는 것을 제한하며, 종속성을 줄여주고, 통제의 집중화를 구현할 수 있다.


쉽게 헷갈릴 수 있는 Facade 패턴과 비교하면, Facade는 단방향(1:M)이지만, Mediator는 양방향(N:M)이며, Facade는 Facade를 통해서만 갈 수 있도록 통로 역할과 연결만 할 뿐 특별한 로직은 가지고 있지 않다. 반면, Mediator는 로직을 가지고 있기 때문에 행위패턴으로 분류되어 있다.

 

 

* Colleague는 상호간에 연결하고자 하는 본 클래스로 Mediator를 통해 연결된다.

 

이 중재자 패턴은 커피 자동판매기 시뮬레이션 프로그램에서 사용할 수 있으며, 램프, 믹서, 동전박스, 지폐박스 간의 커뮤니케이션이 필요한 경우에 중재 클래스를 도입하여 직접적인 관계를 끊고 M:1 관계로 전환할 수 있다.


18. 메멘토 패턴(Memento)

 

Memento 패턴은 오브젝트 내부상태 값을 미리 기록해서 저장해 두었다가 복구할 수 있도록 하는 패턴이다.

 

메멘토라는 의미에 맞게 상태만을 따로 저장(단일 역할의 원칙)하여 작업취소를 가능하게 해주지만, 이전에 상태값을 보관하여야 하여 오버헤드가 발생한다는 단점을 가진다.

 

 

-State : 상태값 변수
-GetState( ), SetState( ) : 이용자 오퍼레이션
-Originator : 복구의 대상
-Caretaker : 메멘토의 보관을 책임지기는 하지만, 메멘토의 내용을 확인하거나 처리하지는 않음

 

이 메멘토 패턴을 이용하여 바둑게임에서 특정 시점의 객체 상태 복원을 통해 무르기 기능을 지원할 수 있다.


19. 옵저버 패턴(Observer)

 

Observer 패턴은 1:M 관계의 오브젝트에 대해 감시하고 있다가 한 객체의 상태가 변하면 다른 모든 객체에게 그 사항을 알리고(Notify), 필요한 경우 자동적으로 수정(Update)이 이루어 지도록 하는 패턴이다.

 

상태가 변할 때 의존자들에게 알리거나 자동 업데이트하며, 주제(Subject)와 Observer의 느슨한 연결을 제공한다.

 


-Push(보냄) / Pool(가져가는 쪽에서 지켜보다가 가져감)

 

이 옵저버 패턴은 엑셀 통계프로그램에서 정보를 수정하면, 그래프 양식도 자동으로 변경하고자 할 때 사용할 수 있다.


20. 상태 패턴(State)

 

State 패턴은 객체 자신의 내부 상태에 따라 행위를 변경하도록 하는 패턴이다. 

자판기에서 커피가 떨어지는 원리와 같이 런타임 시 행위가 바뀌어야 할 때, 조치를 취할 수 있도록 한다.

 

 

이 상태 패턴은 격투게임에서 객체 상태 추가에 따른 행위를 변경하고자 할 때, 사용 가능하다.


21. 전략 패턴(Strategy)

 

Strategy 패턴은 다양한 알고리즘이 존재하면 이들 각각을 하나의 독립적인 클래스로 캡슐화하여 상호교환이 가능하도록 하는 패턴으로, 알고리즘에 대해 확장, 변형이 용이하다는 장점을 가지고 있다.

 

동일 목적의 여러 알고리즘 중에서 원하는 알고리즘을 선택해서 적용하고자 할 경우 사용 가능하며,
클래스 상속구조를 정의해 두고 새로 추가하고 싶은 것은 하위 클래스 형태로 추가할 수 있다.

 


이 전략 패턴은 주로 정렬 알고리즘의 선택에 사용된다.


22. 템플릿메소드 패턴(Template Method)

 

Template Method 패턴은 알고리즘에 대한 골격 구조(템플릿)를 정의하고, 구체적인 단계는 서브클래스에서 정의하는 패턴이다.

 

한번 정의하고 유지되는 경우는 부모에서 정의하고, 변경되는 부분은 서브클래스에서 정의(공통적인 것과 변경될 것들을 나누어서 반영)하도록 하는데,

템플릿 안에 들어가는 메소드는 서브클래스에 의해 바뀌도록 하고(코드 재사용), Hook Operation(로직만 ReDefine할 수 있게 하는 오퍼레이션)을 이용한다.

 

 

이 템플릿메소드 패턴은 HTTP, SMTP 클라이언트 프로그램에서 사용 가능하며, 동일한 골격 하에서 동작 시, 기본 골격은 일괄적으로 관리하고 달라지는 부분은 쉽게 수정하고자 할 때 사용한다.

기본 개념은 상속관계와 오버라이딩을 활용한 재정의이며, 알고리즘 기본 골격의 재사용과 상세 구현 변경이 핵심이다.


23. 방문자 패턴(Visitor)


Visitor 패턴은 연산을 수행해야할 원소의 클래스를 수정하지 않고도 새로운 연산을 추가 가능하게 하는 패턴을 말한다.

 

데이터에 대한 구조(멤버변수)와 처리(메소드)를 분리하여 유지보수를 쉽게 할 수 있도록, 오퍼레이션(메소드)을 별도의 Visitor 클래스에 새롭게 정의한다.

(원래는 자기가 가지고 있어야 하지만, 방문을 통해 수행할 수 있도록 구조를 만듦. 자신의 부모가 아니더라도 그 객체의 구조를 순회하면서 수행 가능)
다만, 캡슐화가 중요하지 않은 경우에만 사용해야 한다는 문제점이 있다.

(데이터 구조와 메소드를 함께 가지고 있는 객체지향의 캡슐화 위배)

 

 

-Visitor : 각 ConcreteElement 클래스에 대한 Visit 오퍼레이션을 선언,

             방문해야 할 방문자 클래스들을 모아놓음
-ConcreteVisitor : Visitor 클래스에 정의된 오퍼레이션을 구현
-Element : 원래 수행되어야 할 클래스
-accept( ) : accept에서 Visitor 클래스를 정의
-ObjectStructure : 요소들을 나열하고 방문자로 하여금 이들 요소에 접근하게 하는 인터페이스를 제공

 

비지터 패턴은 은행 전산 프로그램에서 계좌 종류별로 입금, 출금 등 다양한 작업의 추가/변경이 가능하도록 할 경우, 작업 대상과 작업 항목을 분리하여 클래스 구조로 정의할 수 있다.(효율적 추가/변경)