본문 바로가기

Programming

디자인 패턴 설명 (Design Pattern)





 

Design Pattern은 크게 4가지 분류로 요약해 볼 수 있다. 1. Creational Patterns, 2. Structural Patterns, 3. Behavioral Patterns, 4. Concurrency patterns 이다. 그리고 각 분류마다 다양한 Pattern들이 있다.

대략 38개 정도의 정형화 되어 있는 Pattern들이 있고, 이 보고서에서는 1. – 3. 에 해당하는 Category의 대표적인 13개의 Pattern들에 대해서만 언급하고자 한다.


1.1    Creational Patterns

 

1.1.1       Factory Method Pattern

-       Pattern에서는 객체를 생성하기 위한 Interface를 정의하는데, 어떤 클래스의 Instance를 만들지는 Subclass에서 결정하게 만듭니다. Pattern을 이용하여 구상 형식의 Instance를 만드는 작업을 캡슐화 할 수 있습니다. 밑에 있는 클래스 다이어그램을 보면, Creator추상 클래스에서 객체를 만들기 위한 Method, Factory Method를 위한 인터페이스를 제공한다는 것을 알 수 있습니다. Creator 추상 클래스에 구현되어 있는 다른 Method에서는 Factory Method에 의해 생산된 제품을 가지고 필요한 작업을 처리합니다. 하지만 실제 Factory Method를 구현하고 제품(객체 Instance)을 만들어내는 일은 Subclass에서만 할 수 있습니다.

 


 <Factory Method Pattern>

 

1.1.2       Singleton Pattern

-       해당 클래스의 Instance가 하나만 만들어지고, 어디서든지 그 Instance에 접근할 수 있도록 하기 위한 Pattern

Singleton Pattern을 이용할 때, 주의해야 할 점이 있는데, Multi Threading 환경에서는 이 객체가 유일하게 한 개만 만들어지게 하려면, 다음과 같은 방법을 이용하면 된다.

A.    getInstance() 라는 instance를 얻는 Method를 동기화 시키는 방법

B.     처음부터 instance를 만들어버린 뒤, 사용하는 방법

C.     DCL(Double-Checking Locking)을 써서 getInstance() 에서 동기화 되는 부분을 줄이는 방법, 이 방법은 JDK 1.5 이상 버전에서만 작동한다.

 

 

1.2    Structural Patterns

1.2.1       Decorator Pattern

-       객체에 추가적인 요건을 동적으로 첨가한다. Decorator Pattern Subclass를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.

 


 

<Decorator Pattern>



1.2.2       Adapter Pattern

-       한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환 합니다. Adapter를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있습니다.

Adaptee를 새로 바뀐 인터페이스로 감쌀 때는 객체구성(Composition)을 사용합니다. 이렇게 함으로써, Adaptee의 어떤 Subclass에 대해서도 Adapter를 쓸 수 있다는 장점을 얻을 수 있다.

Pattern에서는 Client를 특정 구현이 아닌 Interface에 연결 시킵니다. 각각 서로 다른 Backend Class들로 변환시키는 여러 Adapter를 사용할 수도 있습니다. 이렇게 Interface를 기준으로 Coding했기 때문에 Target Interface만 제대로 지킨다면 나중에 다른 구현을 추가하는 것도 가능하다.

 


<Adapter Pattern>


1.2.3       Facade Pattern

-       어떤 Sub System의 일련의 인터페이스에 대한 통합된 인터페이스를 제공합니다. Facade에서 고수준 인터페이스를 정의하기 때문에 Sub System을 더 쉽게 사용할 수 있습니다.



<Facade Pattern>


 

1.2.4       Composite Pattern

-       Pattern을 이용하면 객체들을 Tree 구조로 구성하여 부분과 전체를 나타내는 계층구조로 만들 수 있습니다. Pattern을 이용하면 Client에서 개별 객체와 다른 객체들로 구성된 복합 객체(Composite)를 똑 같은 방법으로 다룰 수 있습니다.

 


<Composite Pattern>


1.2.5       Proxy Pattern

-       어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 Pattern

 


<Proxy Pattern>


1.3    Behavioral Patterns

1.3.1       Strategy Pattern

-       알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. Pattern을 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.

 


<Strategy Pattern>

 

1.3.2       State Pattern

-       객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있습니다. 클래스가 바뀌는 것과 같은 결과를 얻을 수 있다.

 

<State Pattern>


1.3.3       Observer Pattern

-       한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many)의존성을 정의. 일대다 관계는 Subject Observer에 의해 정의 됩니다. Observer Subject에 의존합니다. Subject의 상태가 바뀌면 Observer한테 연락이 간다.

 

<Observer Pattern>


 

1.3.4       Command Pattern

-       Pattern을 이용해서 요구사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러 가지 다른 요구 사항을 집어넣을 수도 있습니다. 또한 요청 내역을 Queue에 저장하거나 Log로 기록할 수도 있으며, 작업취소 기능도 지원 가능합니다.

 

<Command Pattern>


 

1.3.5       Template Method Pattern

-       Method에서 알고리즘의 골격을 정의합니다. 알고리즘의 여러 단계 중 일부는 Subclass에서 구현할 수 있습니다. Template Method를 이용하면 알고리즘의 구조는 그대로 유지하면서 Subclass에서 특정 단계를 재정의 할 수 있습니다. , 일련의 단계들로 알고리즘을 정의한 Method이다. 여러 단계 가운데 하나 이상이 추상 Method로 정의되며, 그 추상 Method Subclass에서 구현이 된다. 이렇게 하면 Subclass에서 일부분을 구현할 수 있도록 하면서도 알고리즘의 구조는 바꾸지 않아도 되도록 할 수 있다.

 

<Template Method Pattern>


Example Code. (Code from Wikipedia)

/**

 * An abstract class that is common to several games in

 * which players play against the others, but only one is

 * playing at a given time.

 */

 

abstract class Game {

 

    private int playersCount;

 

    abstract void initializeGame();

 

    abstract void makePlay(int player);

 

    abstract boolean endOfGame();

 

    abstract void printWinner();

 

    /* A template method : */

    final void playOneGame(int playersCount) {

        this.playersCount = playersCount;

        initializeGame();

        int j = 0;

        while (!endOfGame()) {

            makePlay(j);

            j = (j + 1) % playersCount;

        }

        printWinner();

    }

}

 

//Now we can extend this class in order to implement actual games:

class Monopoly extends Game {

 

    /* Implementation of necessary concrete methods */

 

    void initializeGame() {

        // ...

    }

 

    void makePlay(int player) {

        // ...

    }

 

    boolean endOfGame() {

        // ...

    }

 

    void printWinner() {

        // ...

    }

 

    /* Specific declarations for the Monopoly game. */

 

    // ...

 

}

 

class Chess extends Game {

 

    /* Implementation of necessary concrete methods */

 

    void initializeGame() {

        // ...

    }

 

    void makePlay(int player) {

        // ...

    }

 

    boolean endOfGame() {

        // ...

    }

 

    void printWinner() {

        // ...

    }

    /* Specific declarations for the chess game. */

 

    // ...

}


 

1.3.6       Iterator Pattern

-       Collection 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 수 있게 해주는 방법을 제공해 준다.

 

<Iterator Pattern>