'behavioral pattern'에 해당되는 글 10건

  1. 2012.07.05 Template method pattern
  2. 2012.07.04 Strategy pattern
  3. 2012.07.03 State pattern
  4. 2012.06.27 Observer pattern
  5. 2012.06.20 Memento pattern
  6. 2012.06.19 Mediator pattern
  7. 2012.06.18 Iterator pattern
  8. 2012.06.15 Interpreter pattern
  9. 2012.06.15 Command Pattern
  10. 2012.06.08 Chain-of-responsibility pattern

먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.

영어실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문주소 : http://en.wikipedia.org/wiki/Template_method_pattern



Behavioral - Template method pattern

소프트웨어 공학(software engineering)에서 template method pattern디자인 패턴(design pattern) 중 하나이다. 행위적 패턴(behavioral pattern) 중 하나이고, C++ templates와는 무관하다.

도입 Introduction

template method 는 특정 알고리즘프로그램 스켈레톤(program skeleton)을 정의한다. 하나 이상의 알고리즘 단계들이 하위 클래스(서브 클래스)에서 재구현(overriden)될 수 있는데, 이 때 중요한 뼈대가 되는 알고리즘은 그대로 유지한체 다른 동작을 할수 있도록 한다.


객체 지향 프로그래밍에서 한 클래스는 특정 알고리즘 설계(algorithm design)의 기본적인 단계들을 제공하도록 만들어진다. 이러한 단계들은 추상메서드(abstract methods)를 사용해서 구현된다. 서브클래스에서는 추상 메서드를 실제로 동작하게 끔 구현한다. 뼈대에 해당하는 알고리즘은 유지한채로 서브 클래스에서 각 단계들의 구현만 달라지게 된다.


template method 패턴은 의미(semactics)에 해당하는 큰 그림과 세부 구현, 그리고 일련의 메서드들을 다룬다. 이런 큰 그림이 추상 메서드와 비추상(non-abstract) 메서드를 호출한다. 비추상 메서드는 template method에 의해 전적으로 처리되지만, 서브 클래스에서 구현되는 추상 메서드는 이 패턴의 힘과 자유도를 제공한다. 추상 메서드의 일부 또는 전체는 서브 클래스에서 구현이 되는데, 서브 클래스를 작성하는 사람이 더 큰 의미론적으로는 변화를 최소화시킨 채 특정 행동을 할 수 있도록 해준다. 


template method 패턴은 자주 사용된다. 한 메서드가 오직 하나의 추상 메서드를 호출하는 경우는 정말 최소한으로 template method를 사용한 케이스라 볼 수 있다. 어떤 개발자가 다형성(polymorphic)을 전적으로 사용하고 있다면, 이 디자인 패턴은 꽤나 자연스러운 결과이다. template method는 지금 당장 존재하는 값을 소프트웨어에 추가하기 위해 사용되거나 미래에 더 강화된 기능을 위해 사용된다. 


template method 패턴은 Non-Virtual Interface(NVI) 패턴과 밀접한 관련이 있다. NVI 패턴은 비추상 메서드가 추상 메서드를 호출하는 것의 장점을 잘 알고 있다. NVI 패턴은 아주 작은 소프트웨어 생산과 런타임 비용을 들게 할 수 있다. 많은 상업적 소프트웨어 프레임웍(software frameworks)에서는 NVI 패턴을 채용하고 있다.


Template method 패턴은 Adapter Pattern에서 하듯이 GRASP를 구현한다. 다른 점은 Adapter는 여러 오퍼레이션에게 같은 인터페이스를 제공하지만, Template Method는 오직 하나에게만 제공한다는 점이다.

구조 Structure



사용법 Usage

Template method는 다음과 같은 경우에 사용된다.

  • 변할 수 있는 행위에 대한 구현은 서브 클래스에서 메서드 오버라이딩(method overriding)을 통해 하도록 한다.
  • 코드의 중복 제거 : 추상 클래스의 알고리즘에서 일반적인 흐름 구조는 한번만 구현되고, 필요한 변화는 각 서브클래스에서 구현된다.
  • 어떤 지점에서 서브 클래싱(subclassing)이 가능한지를 조절할 수 있다. 흐름에 대해 근본적인 부분부터 다 바꿔버리는 단순한 다형성 오버라이드와는 반대로, 흐름의 일부에 대해서만 변화를 허용한다.


template 패턴을 사용한 애플리케이션의 결과인 조절 구조(inversion of control)헐리웃 원리(Hollywood Principle, "날 부르지마, 내가 널 부를테니..") 같다고 언급된다. 이 원리를 이용하면 부모 클래스에 있는 template method는 서브클래스의 메서드를 필요할 때 호출함으로써 전반적인 프로세스를 관리한다.  다음의 자바 예를 보면 알 수 있다. 


예 Example

/**
 * 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 {
 
    protected int playersCount;
    abstract void initializeGame();
    abstract void makePlay(int player);
    abstract boolean endOfGame();
    abstract void printWinner();
 
    /* A template method : */
    public 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() {
        // Initialize players
        // Initialize money
    }
    void makePlay(int player) {
        // Process one turn of player
    }
    boolean endOfGame() {
        // Return true if game is over 
        // according to Monopoly rules
    }
    void printWinner() {
        // Display who won
    }
    /* Specific declarations for the Monopoly game. */
 
    // ...
}
 
class Chess extends Game {
 
    /* Implementation of necessary concrete methods */
    void initializeGame() {
        // Initialize players
        // Put the pieces on the board
    }
    void makePlay(int player) {
        // Process a turn for the player
    }
    boolean endOfGame() {
        // Return true if in Checkmate or 
        // Stalemate has been reached
    }
    void printWinner() {
        // Display the winning player
    }
    /* Specific declarations for the chess game. */
 
    // ...
}


저작자 표시 비영리 변경 금지
신고

'Design Patterns > 영문 위키(Wikipedia)' 카테고리의 다른 글

Template method pattern  (0) 2012.07.05
Strategy pattern  (0) 2012.07.04
State pattern  (0) 2012.07.03
Observer pattern  (0) 2012.06.27
Memento pattern  (0) 2012.06.20
Mediator pattern  (0) 2012.06.19
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.
영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/Strategy_pattern



Behavioral - Strategy Pattern


컴퓨터 프로그래밍(computer programming)에서 policy pattern이라고도 알려져 있는 strategy pattern은 런타임 시에 알고리즘(algorithms)을 선택할 수 있는 특별한 디자인 패턴(software design pattern) 중 하나이다. 형식적으로 말하면 strategy 패턴은 알고리즘 군(family)을 정의하고, 각각을 캡슐화해서 서로 바꿔가며 쓸 수 있게 한다. strategy는 그를 사용하는 클라이언트 코드로부터 알고리즘을 독립적으로 바꿀 수 있게 해준다. 


예를 들어, 어떤 데이터의 유효성을 체크하는 클래스가 있는데, 이 클래스는 데이터의 타입이나 사용자의 선택 등의 특정 인자에 따라 유효성 체크 알고리즘을 선택하기 위해 strategy 패턴을 사용할 수 있다. 이러한 인자는 실제 그 상황이 되기 전까지는 아무 것도 모르고, 그 상황마다 유효성 체크 알고리즘이 완전히 달라질 수도 있다. 

유효성을 체크하는 객체로부터 분리되어 캡슐화되는 유효성 체크 전략(strategies)은 시스템의 다른 영역에서 코드 중복없이 사용될 수 있다 .


프로그래밍 언어(programming language)에서 필수적인 요소는 어떤 자료 구조 내의 코드에 대한 참조를 가지고 있다가 필요할 때 사용하는 것이다. 이는 function pointer, first-class function, 객체지향 프로그래밍 언어에서 클래스나 클래스 인스턴스, 또는 reflection을 통한 언어 구현의 내부 코드에 대한 접근같은 메커니즘으로 성취할 수 있는 부분이다. 

구조 Structure



예 Example

다음은 Java의 한 예이다.


// The classes that implement a concrete strategy should implement this.
// The Context class uses this to call the concrete strategy.
interface Strategy {
    int execute(int a, int b); 
}
 
// Implements the algorithm using the strategy interface
class ConcreteStrategyAdd implements Strategy {
 
    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategyAdd's execute()");
        return a + b;  // Do an addition with a and b
    }
}
 
class ConcreteStrategySubtract implements Strategy {
 
    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategySubtract's execute()");
        return a - b;  // Do a subtraction with a and b
    }
}
 
class ConcreteStrategyMultiply implements Strategy {
 
    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategyMultiply's execute()");
        return a * b;   // Do a multiplication with a and b
    }    
}
 
// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
class Context {
 
    private Strategy strategy;
 
    // Constructor
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
 
    public int executeStrategy(int a, int b) {
        return strategy.execute(a, b);
    }
}
 
// Test application
class StrategyExample {
 
    public static void main(String[] args) {
 
        Context context;
 
        // Three contexts following different strategies
        context = new Context(new ConcreteStrategyAdd());
        int resultA = context.executeStrategy(3,4);
 
        context = new Context(new ConcreteStrategySubtract());
        int resultB = context.executeStrategy(3,4);
 
        context = new Context(new ConcreteStrategyMultiply());
        int resultC = context.executeStrategy(3,4);
    }
}

Strategy versus Bridge

strategy 패턴의 UML 클래스 다이어그램은 Bridge 패턴의 다이어그램과 같다. 하지만 이 둘은 의도(목적, intent)가 다르다. strategy 패턴은 행위(behavior)와 관련있지만, Bridge 패턴은 구조(structure)와 관련있다. 

context와 strategy간의 커플링이 Bridge패턴의 abstraction와 implementation간의 커플링보다 더 강하다.

Strategy and open/closed principle

strategy 패턴에 따르면, 클래스의 행위는 상속되는 것보다는 인터페이스를 이용해서 캡슐화되어야 한다. 예를들어 car클래스를 생각해보자. car의 두 가지 가능한 기능은 정지(brake)와 가속(accelerate)이다. 


정지와 가속은 자동차 모델에 따라 자주 바뀔 수 있는 부분이기 때문에, 이를 구현하기 위한 일반적인 접근은 상속이다. 이 방법은 중대한 단점이 있다. 가속과 정지는 새로운 자동차 모델마다 새로 정의되어야 한다. 이는 자동차 모델의 개수가 늘어날 수록 관리해야하는 부분이 많아지고, 모델간의 코드 중복되 많아진다. 게다가 각각의 모델에 코드를 추가하지 않기도 어렵다. 


strategy 패턴에서는 상속 대신에 집합(aggregation)을 이용한다. strategy패턴에서 행위(behaviors)는 여러 인터페이스와 그 인터페이스를 구현하는 클래스들로 정의된다. 각 클래스는 이 인터페이스를 캡슐화한다. 이는 행위와 그 행위를 사용하는 클래스 사이의 커플링을 감소시킨다. 행위는 그 행위를 사용하는 클래스의 변화없이 바뀔 수 있고, 심한 코드 변화없이도 행위를 서로 바꿀 수 있다.  행위는 디자인(설계) 시점 뿐만 아니라 런타임 시에도 바뀔 수 있다. 예를 들어, car 객체의 정지 행위는 brakeBehavior 멤버를 다음과 같이 바꿈으로써 BrakeWithABS()에서 Brake()으로 바뀔 수 있다.

brakeBehavior = new Brake();


이는 설계에서 엄청난 유연성을 제공하고, "클래스는 확장에 열려있어야 하고 수정에는 닫혀있어야 한다"고 말하는 Open/Closed Princicle(OCP)와 잘 어울린다.

저작자 표시 비영리 변경 금지
신고

'Design Patterns > 영문 위키(Wikipedia)' 카테고리의 다른 글

Template method pattern  (0) 2012.07.05
Strategy pattern  (0) 2012.07.04
State pattern  (0) 2012.07.03
Observer pattern  (0) 2012.06.27
Memento pattern  (0) 2012.06.20
Mediator pattern  (0) 2012.06.19
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/State_pattern



Behavioral - State Pattern

Strategy Pattern과 매우 닮은 state 패턴행위적 디자인 패턴(behavioral software design pattern)인데, 상태를 위한 객체 패턴(objects for states pattern)이라고도 알려져 있다. 이 패턴은 어떤 객체의 상태를 나타내기 위해 컴퓨터 프로그래밍(computer programming)에서 사용된다. 이는 런타임시에 어떤 객체를 부분적으로 변화시키기에 명확한 방법이다. 

구조 Structure



예 Example

의사 코드 Pseudocode

예를들어 그림을 그리는 프로그램을 생각해보자. 이 프로그램에는 마우스 커서가 있는데, 이 커서는 특정 시점에 여러 도구 중 하나로 동작할 수 있다. 커서 객체를 각 상황에 맞게 바꾸는 것 대신에, 커서가 내부 상태를 가지고 있고, 이 상태가 현재 사용하고 있는 도구를 나타낸다. 도구에 의존적인 메서드가 호출될때 (예를들면 마우스 클릭), 이 메서드 호출은 커서의 상태에 전달이 된다. 

각 도구는 하나의 상태를 나타낸다. 추상 상태 클래스는 AbstractTool이다. 


 class AbstractTool is
     function moveTo(point) is
         input:  the location point the mouse moved to
         (this function must be implemented by subclasses)
 
     function mouseDown(point) is
         input:  the location point the mouse is at
         (this function must be implemented by subclasses)
 
     function mouseUp(point) is
         input:  the location point the mouse is at
         (this function must be implemented by subclasses)

이 정의에 따르면, 각 도구는 마우스 커서의 움직임과 클릭이나 드래그에 대한 시작점과 끝점에 대해서도 처리해야 한다.
베이스(추상) 클래스를 이용해서 간단한 펜 도구와 선택 도구를 다음과 같이 만들 수 있다.


subclass PenTool of AbstractTool is
     last_mouse_position := invalid
     mouse_button := up
 
     function moveTo(point) is
         input:  the location point the mouse moved to
         if mouse_button = down
             (draw a line from the last_mouse_position to point)
             last_mouse_position := point
 
     function mouseDown(point) is
         input:  the location point the mouse is at
         mouse_button := down
         last_mouse_position := point
 
     function mouseUp(point) is
         input:  the location point the mouse is at
         mouse_button := up  

 subclass SelectionTool of AbstractTool is
     selection_start := invalid
     mouse_button := up
 
     function moveTo(point) is
         input:  the location point the mouse moved to
         if mouse_button = down
             (select the rectangle between selection_start and point)
 
     function mouseDown(point) is
         input:  the location point the mouse is at
         mouse_button := down
         selection_start := point
 
     function mouseUp(point) is
         input:  the location point the mouse is at
         mouse_button := up

이 예에서 context를 위한 클래스는 Cursor라 불린다. 추상 상태 클래스(여기에서는 AbstractTool)의 메서드들은 context 안에서도 모두 구현된다. context클래스에서 이 메서드들은 현재 상태(Cursor클래스에서 current_tool로 표시)의 관련 메서드를 호출한다.


class Cursor is
     current_tool := new PenTool
 
     function moveTo(point) is
         input:  the location point the mouse moved to
         current_tool.moveTo(point)
 
     function mouseDown(point) is
         input:  the location point the mouse is at
         current_tool.mouseDown(point)
 
     function mouseUp(point) is
         input:  the location point the mouse is at
         current_tool.mouseUp(point)
 
     function usePenTool() is
         current_tool := new PenTool
 
     function useSelectionTool() is
         current_tool := new SelectionTool

Cursor객체가 어떻게 다른 시점에 PenTool과 SelectionTool로 동작할 수 있는 지에 주목하자. Cursor는 어떤 툴이 활성화되어 있든지 상관없이 적절한 메서드를 호출한다. 이것이 state pattern의 정수(essence)이다. 여기에서 우리는 단순한 상속만을 이용한 PenCursor와 SelectCursor 클래스를 만들어서 상태와 객체를 결합시킬 수도 있었다. 하지만 Cursor는 새로운 툴이 선택될 때마다 비싸거나 새로운 객체로 복사하는데 우아하지 않은 데이터를 옮길 수도 있다. 


Java

상태 인터페이스와 두개의 구현. 상태의 메서드는 context객체에 대한 참조를 가지고 있고, 상태를 바꿀 수도 있다.

interface State {
        void writeName(StateContext stateContext, String name);
}
 
class StateA implements State {
        public void writeName(StateContext stateContext, String name) {
                System.out.println(name.toLowerCase());
                stateContext.setState(new StateB());
        }
}
 
class StateB implements State {
        private int count=0;
        public void writeName(StateContext stateContext, String name){
                System.out.println(name.toUpperCase());
                // change state after StateB's writeName() gets invoked twice
                if(++count>1) {
                        stateContext.setState(new StateA());
                }
        }
}

context 클래스는 상태 변수를 가지고 있는데, 이 상태 변수는 초기 상태에서 인스턴스화된다. 여기에서는 StateA가 초기 상태이다. 이 메서드에서 상태 객체의 관련된 메서드를 사용하는 것을 볼 수 있다.

public class StateContext {
        private State myState;
        public StateContext() {
                setState(new StateA());
        }
 
        // normally only called by classes implementing the State interface
        public void setState(State newState) {
                this.myState = newState;
        }
 
        public void writeName(String name) {
                this.myState.writeName(this, name);
        }
}

그리고 다음과 같이 사용한다.

public class TestClientState {
        public static void main(String[] args) {
                StateContext sc = new StateContext();
                sc.writeName("Monday");
                sc.writeName("Tuesday");
                sc.writeName("Wednesday");
                sc.writeName("Thursday");
                sc.writeName("Saturday");
                sc.writeName("Sunday");
        }
}

위의 코드에서 TestClientState클래스의 main()메서드의 결과는 다음과 같다.

monday
TUESDAY
WEDNESDAY
thursday
SATURDAY
SUNDAY
저작자 표시 비영리 변경 금지
신고

'Design Patterns > 영문 위키(Wikipedia)' 카테고리의 다른 글

Template method pattern  (0) 2012.07.05
Strategy pattern  (0) 2012.07.04
State pattern  (0) 2012.07.03
Observer pattern  (0) 2012.06.27
Memento pattern  (0) 2012.06.20
Mediator pattern  (0) 2012.06.19
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/Observer_pattern



Behavioral - Observer pattern

옵저버(observer) 패턴은 subject라 불리는 객체(object)가 observer라 불리는 객체들의 리스트를 관리하고, 상태변화가 있으면 알아서 옵저버 객체들에게 알려주는(종종 옵저버의 메서드 methods를 호출함으로써 알려주는) 디자인 패턴(software design pattern)이다. 

이 패턴은 주로 분산된 이벤트 처리(event handling) 시스템에서 사용된다. 또한 옵저버는 MVC 패턴의 중요한 부분이기도 하다.

사실, 옵저버는 Smalltalk의 MVC 기반 UI 프레임웍에서 가장 먼저 구현되었다.


관련된 패턴들 : Publish-subscribe pattern, mediator, singleton

구조 Structure


정의 Definition

옵저버 패턴의 정수(essence)는 다음과 같다.

객체 사이에 일대다(one-to-many) 관계를 정의함으로써, 한 객체의 상태가 변경되었을 때 의존하는(dependents) 객체들에게 자동으로 알려주고 업데이트하도록 하기 위함이다.

예 Example

아래는 키보드 입력을 받아서 한줄 마다 이벤트로 처리하는 예이다. 이 예는 java.util.Observerjava.util.Observable 이 두개의 라이브러리 클래스를 사용해서 만들었다. System.in 으로부터 문자열이 들어오면 notifyObservers()메서드가 호출되고, 그러면 모든 옵저버들의 update메서드를 호출함으로써(이 예에서는 ResponseHandler.update메서드) 이벤트의 발생을 알린다. 


MyApp.java파일에 코드를 실행시킬 수 있는 main()메서드가 있다.


/* File Name : EventSource.java */
package org.wikipedia.obs;
 
import java.util.Observable;          //Observable is here
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class EventSource extends Observable implements Runnable {
    public void run() {
        try {
            final InputStreamReader isr = new InputStreamReader(System.in);
            final BufferedReader br = new BufferedReader(isr);
            while (true) {
                String response = br.readLine();
                setChanged();
                notifyObservers(response);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/* File Name: ResponseHandler.java */
 
package org.wikipedia.obs;
 
import java.util.Observable;
import java.util.Observer;  /* this is Event Handler */
 
public class ResponseHandler implements Observer {
    private String resp;
    public void update(Observable obj, Object arg) {
        if (arg instanceof String) {
            resp = (String) arg;
            System.out.println("\nReceived Response: " + resp );
        }
    }
}

/* Filename : MyApp.java */
/* This is the main program */
 
package org.wikipedia.obs;
 
public class MyApp {
    public static void main(String[] args) {
        System.out.println("Enter Text >");
 
        // create an event source - reads from stdin
        final EventSource eventSource = new EventSource();
 
        // create an observer
        final ResponseHandler responseHandler = new ResponseHandler();
 
        // subscribe the observer to the event source
        eventSource.addObserver(responseHandler);
 
        // starts the event thread
        Thread thread = new Thread(eventSource);
        thread.start();
    }
}

구현 Implements

옵저버 패턴은 거의 대부분의 GUI 툴킷을 포함한 많은 프로그래밍 라이브러리(programming libraries)와 시스템에서 구현되어 있다. 아래와 같은 주목할만한 구현들이 있다.

ActionScript


C


C++


Objective-C


C#


ColdFusion


Delphi


Java

  • java.util.Observer 클래스에서 간단한 옵저버 구현을 제공하고 있다.
  • 전형적으로 자바에서 이벤트는 callback 패턴으로 구현되어 있다. : 한 부분은 공통 인터페이스를 제공하고, 다른 부분은 그 인터페이스를 구현하고, 또 다른 부분은 인터페이스에 대한 레퍼런스를 가지고 있다가 이벤트를 발생시킨다. 


JavaScript

  • EventDispatcher singleton
  • DevShop
  • jQuery에서 이벤트 핸들러를 만들기 위해 '.on()'을 쓰고, 이벤트를 발생시키기 위해 '.trigger()'를 쓰고, 이벤트 핸들러를 제거하기 위해'off()'를 사용하도록 되어 있는데, 이 것이 옵저버 패턴을 구현한 부분이다. jQuery에서는 실제 메모리에 DOM 요소를 올려서 사용하는데, 이벤트 핸들러는 DOM구조에 붙였다가 DOM의 해당 요소가 삭제되면 자동적으로 이벤트도 삭제된다. 더 많이 알고 싶다면 jQuery Event Model 슬라이드 쇼를 봐라.


Lisp


Perl


PHP


Python


Ruby

  • Observer - 루비 표준 라이브러리에 있다.

Other/Misc


저작자 표시 비영리 변경 금지
신고

'Design Patterns > 영문 위키(Wikipedia)' 카테고리의 다른 글

Strategy pattern  (0) 2012.07.04
State pattern  (0) 2012.07.03
Observer pattern  (0) 2012.06.27
Memento pattern  (0) 2012.06.20
Mediator pattern  (0) 2012.06.19
Iterator pattern  (0) 2012.06.18
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글은 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/Memento_pattern



Behavioral - Memento pattern



memento 패턴은 객체의 이전 상태를 저장하는 방법을 제공하는 소프트웨어 디자인 패턴(software design pattern)이다. (롤백을 통한 취소(undo)와 같은 예가 있다.)


memento 패턴은 originator(창작자, 시조)와 caretaker(경비원, 관리인)라는 두개의 객체로 구현된다. Originator는 내부 상태(state)를 가지고 있는 객체이다. Caretaker는 originator에게 어떤 동작을 하지만 변화에 대해 취소하고 싶어한다. caretaker는 먼저 originator에게 memento객체를 요청한다. 그러면 caretaker가 어떤 일을 하려고했던지 상관없이 그 동작을 수행한다. 동작이 일어나기 전 상태로 되돌리기 위해 caretaker는 originator에게 memento객체를 반환한다. memento객체는 불투명한 객체(opaque object)이다. (caretaker는 memento객체를 변경할 수 없기 때문에). 이 패턴을 사용할 때 originator가 다른 객체나 자원(resources)을 변경할 수 있다면 주의를 기울여야 한다.(memento 패턴은 한 특정 객체안에서 동작한다.)


memento 패턴에 대한 전형적인 예에는 pseudorandom number generator 의 씨앗(seed, 랜덤 숫자를 생성할 때 사용하는 일종의 key라고 생각하면 되겠네요.)과 finite state machine의 상태가 있다.

예 Example

아래의 예는 Memento 패턴의 "undo"사용법을 묘사하고 있는 Java 프로그램이다.

import java.util.List;
import java.util.ArrayList;
class Originator {
    private String state;
    // The class could also contain additional data that is not part of the
    // state saved in the memento.
 
    public void set(String state) {
        System.out.println("Originator: Setting state to " + state);
        this.state = state;
    }
 
    public Memento saveToMemento() {
        System.out.println("Originator: Saving to Memento.");
        return new Memento(state);
    }
 
    public void restoreFromMemento(Memento memento) {
        state = memento.getSavedState();
        System.out.println("Originator: State after restoring from Memento: " + state);
    }
 
    public static class Memento {
        private final String state;
 
        public Memento(String stateToSave) {
            state = stateToSave;
        }
 
        public String getSavedState() {
            return state;
        }
    }
}
 
class Caretaker {
    public static void main(String[] args) {
        List savedStates = new ArrayList();
 
        Originator originator = new Originator();
        originator.set("State1");
        originator.set("State2");
        savedStates.add(originator.saveToMemento());
        originator.set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.add(originator.saveToMemento());
        originator.set("State4");
 
        originator.restoreFromMemento(savedStates.get(1));   
    }
}



결과는 아래와 같다.

Originator: Setting state to State1
Originator: Setting state to State2
Originator: Saving to Memento.
Originator: Setting state to State3
Originator: Saving to Memento.
Originator: Setting state to State4
Originator: State after restoring from Memento: State3



이 예에서는 상태를 표현하기 위해 String 클래스를 사용하고 있다.(이는 자바에서 기본적으로 immutable type(변하지 않는 타입)이다.) 실제 시나리오에서 상태는 대게는 특정 객체가 되어야 한다. 그래서 memento 객체에 넣기 전에 상태객체는 복제해서 넣어야한다.

 private Memento(State state)
        {
            //state has to be cloned before returning the 
            //memento, or successive calls to get Memento
            //return a reference to the same object
            this.mementoState = state.clone();
 
        }


저작자 표시 비영리 변경 금지
신고

'Design Patterns > 영문 위키(Wikipedia)' 카테고리의 다른 글

State pattern  (0) 2012.07.03
Observer pattern  (0) 2012.06.27
Memento pattern  (0) 2012.06.20
Mediator pattern  (0) 2012.06.19
Iterator pattern  (0) 2012.06.18
Interpreter pattern  (0) 2012.06.15
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글을 번역한 글 임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/Mediator_pattern



Behavioral - Mediator pattern


mediator pattern은 한 집합의 객체들이 어떻게 상호작용하는지를 캡슐화 한 객체를 정의한다. 이 패턴은 프로그램이 동작하는 행위를 변경할 수 있기 때문에 behavioral pattern으로 간주된다.


보통 한 프로그램은 많은 클래스들(classes)로 만들어 진다. 그래서 논리(logic)계산(computation)이 이런 클래스들 사이에 분산되어 있다. 하지만 더 많은 클래스들이 만들어질수록 클래스들 간의 의사소통(communication) 문제는 더욱 복잡해진다.(특히 유지보수(maintenance)리펙토링(refactoring)을 할 때에는 더욱 더) 이러한 문제는 프로그램을 읽기 힘들게, 그리고 유지보수(maintain)하기 힘들게 만든다. 게다가, 이런 상황에서 약간의 코드 변화라도 다른 일부 클래스들에게 영향을 끼칠 수 있기 때문에 프로그램을 수정하는 게 어려워질 수 있다.


mediator 패턴을 이용하면, 객체간의 의사소통은 mediator객체에 의해 캡슐화 된다. 객체들은 더 이상 직접적으로 의사소통하지 않고, mediator를 통해서만 의사소통을 한다. 이렇게 함으로써 의사소통하는 객체들간의 의존도(dependencies)를 낮추고 따라서 coupling을 낮추게 된다.

정의 Definition

Mediator 패턴의 정수(essence)는 다음과 같다.

어떤 집합의 객체들이 어떻게 상호작용하는 가에 대해 캡슐화하는 객체를 정의하라. Mediator는 객체들이 서로 참조하지 못하도록 함으로써 낮은 커플링을 촉진시키고, 그 클래스들이 독립적으로 상호작용할 수 있도록 해준다.

참가자 Participants

Mediator - Colleague 객체들과 의사소통하기 위한 인터페이스를 정의한다.

ConcreteMediator - Mediator 인터페이스를 구현하고 Colleague 객체간의 의사소통을 조정한다. 이 클래스는 서로 의사소통하기 위해서 모든 Colleague와 그것의 목적을 다 알고 있다.

ConcreteColleague - Mediator를 통해서 다른 Colleague들과 의사소통한다.

예 Example

아래의 예에서 mediator 객체는 3개의 협력하는 버튼들의 상태를 조절한다.: 이렇게 상태를 조절하기 위해 mediator는 book(), view(), search() 메서더를 가진다. 이 3개의 메서드들은 각 버튼의 excute()메서드에 의해 호출된다. 

이로인해, 여기에서 협동 패턴은 각 참여자(버튼)가 mediator를 통해서 그들의 활동에 대해 의사소통하고, mediator는 다른 참가자에게 기대되는 행위를 전달한다.

import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
 
//Colleague interface
interface Command {
    void execute();
}
 
//Abstract Mediator
interface IMediator {
    void book();
    void view();
    void search();
    void registerView(BtnView v);
    void registerSearch(BtnSearch s);
    void registerBook(BtnBook b);
    void registerDisplay(LblDisplay d);
}
 
//Concrete mediator
class Mediator implements IMediator {
 
    BtnView btnView;
    BtnSearch btnSearch;
    BtnBook btnBook;
    LblDisplay show;
 
    //....
    void registerView(BtnView v) {
        btnView = v;
    }
 
    void registerSearch(BtnSearch s) {
        btnSearch = s;
    }
 
    void registerBook(BtnBook b) {
        btnBook = b;
    }
 
    void registerDisplay(LblDisplay d) {
        show = d;
    }
 
    void book() {
        btnBook.setEnabled(false);
        btnView.setEnabled(true);
        btnSearch.setEnabled(true);
        show.setText("booking...");
    }
 
    void view() {
        btnView.setEnabled(false);
        btnSearch.setEnabled(true);
        btnBook.setEnabled(true);
        show.setText("viewing...");
    }
 
    void search() {
        btnSearch.setEnabled(false);
        btnView.setEnabled(true);
        btnBook.setEnabled(true);
        show.setText("searching...");
    }
 
}
 
//A concrete colleague
class BtnView extends JButton implements Command {
 
    IMediator med;
 
    BtnView(ActionListener al, IMediator m) {
        super("View");
        addActionListener(al);
        med = m;
        med.registerView(this);
    }
 
    public void execute() {
        med.view();
    }
 
}
 
//A concrete colleague
class BtnSearch extends JButton implements Command {
 
    IMediator med;
 
    BtnSearch(ActionListener al, IMediator m) {
        super("Search");
        addActionListener(al);
        med = m;
        med.registerSearch(this);
    }
 
    public void execute() {
        med.search();
    }
 
}
 
//A concrete colleague
class BtnBook extends JButton implements Command {
 
    IMediator med;
 
    BtnBook(ActionListener al, IMediator m) {
        super("Book");
        addActionListener(al);
        med = m;
        med.registerBook(this);
    }
 
    public void execute() {
        med.book();
    }
 
}
 
class LblDisplay extends JLabel {
 
    IMediator med;
 
    LblDisplay(IMediator m) {
        super("Just start...");
        med = m;
        med.registerDisplay(this);
        setFont(new Font("Arial", Font.BOLD, 24));
    }
 
}
 
class MediatorDemo extends JFrame implements ActionListener {
 
    IMediator med = new Mediator();
 
    MediatorDemo() {
        JPanel p = new JPanel();
        p.add(new BtnView(this, med));
        p.add(new BtnBook(this, med));
        p.add(new BtnSearch(this, med));
        getContentPane().add(new LblDisplay(med), "North");
        getContentPane().add(p, "South");
        setSize(400, 200);
        setVisible(true);
    }
 
    public void actionPerformed(ActionEvent ae) {
        Command comd = (Command) ae.getSource();
        comd.execute();
    }
 
    public static void main(String[] args) {
        new MediatorDemo();
    }
 
}




저작자 표시 비영리 변경 금지
신고

'Design Patterns > 영문 위키(Wikipedia)' 카테고리의 다른 글

Observer pattern  (0) 2012.06.27
Memento pattern  (0) 2012.06.20
Mediator pattern  (0) 2012.06.19
Iterator pattern  (0) 2012.06.18
Interpreter pattern  (0) 2012.06.15
Command Pattern  (0) 2012.06.15
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 :  http://en.wikipedia.org/wiki/Iterator_pattern



Behavioral - Iterator pattern



객체지향 프로그래밍(object-oriented programming)에서, 이터레이터 패턴(iterator pattern)반복자(iterator)컨테이너(container)(List, Set, Map등을 말합니다.)를 돌면서 컨테이너의 요소(elements)에 접근하는 디자인 패턴(design pattern)이다. ; 어떤 경우에 컨테이너에 특화된 알고리즘이 필요한 경우가 있고 따라서 이런 부분은 컨테이너와의 독립성을 유지하기 힘들다.


예를 들어, SearchForElement라는 가상의(hypothetical) 알고리즘이 컨테이너에 특화된 알고리즘으로 구현하지 않고, 특정 타입의 반복자(iterator)를 사용해서 구현할 수 있다. 이렇게 하면 SearchForElement는 그 타입의 iterator(앞에서 구현한 특정타입의 반복자)를 지원하는 어떤 컨테이너에서든지 사용할 수 있게 된다. 

정의 Definition

Iterator Factory method pattern의 본질(essence)은 "세부적인 구현(underlying representation)을 노출시키지 않고서, 컨테이너(aggregate object)의 요소에 차례대로 접근할 수 있는 방법을 제공하는 것"이다.

언어에 특화된 구현 Language-specific implementation

메인 글 : Iterator
일부 언어는 문법을 표준화하고 있다. C++과 Phython이 이에 속한다.

C++

C++ 에서는 구분자(iterator)를 pointer의 의미(semantics)로 구현한다. C++에서 어떤 클래스는 포인터의 모든 오퍼레이션을 오버로드할 수 있어서,  반복자에서 역참조(dereference), 증가, 감소 연산을 완벽히 구현할 수 있다. 이는 평범한 메모리 버퍼에 std::sort같은 알고리즘을 바로 사용할 수 있고, 새로 배워야할 문법이 없다는 장점이 있다. 하지만, 컨테이너의 마지막인지 확인하기 위해서는 반복자가 스스로 알지 못하기 때문에 마지막("end") 반복자를 알고 있어야 한다는 단점이 있다. 우리는 C++에서 반복자가 iterator 컨셉(concept)을 본보기로 하고 있다고 말한다.


Python

Python은 언어자체에서 반복자를 하나의 문법으로 지정하고 있어서 for 같은 키워드와 함께 사용된다. sequence에는 __iter__()라는 메서드가 있는데 이 메서드는 반복자 객체를 반환한다. next()메서드를 통해 다음 요소를 반환하거나, sequence의 끝에 도달하면 StopIteration 예외(exception)를 발생시킨다. 반복자도 __iter__()메서드를 가지고 있어서 스스로를 반환시킬 수 있다.


Java

자바는 Collections 클래스가 구현해야만하는 Iterator 인터페이스가 있다. (그래서 자바의 컬렉션들은 모두 Iterator인터페이스를 구현하고 있고, Map이든 Set이든 List든 상관없이 Iterator 인터페이스를 이용해서 모든 요소 검사를 할 수 있죠.)

저작자 표시 비영리 변경 금지
신고

'Design Patterns > 영문 위키(Wikipedia)' 카테고리의 다른 글

Memento pattern  (0) 2012.06.20
Mediator pattern  (0) 2012.06.19
Iterator pattern  (0) 2012.06.18
Interpreter pattern  (0) 2012.06.15
Command Pattern  (0) 2012.06.15
Chain-of-responsibility pattern  (0) 2012.06.08
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/Interpreter_pattern



Behavioral pattern - Interpreter pattern

컴퓨터 프로그래밍(computer programming)에서 interpreter pattern은 어떤 언어에서 어떻게 문장들을 평가할지를 결정하는 디자인 패턴(design pattern)이다. 기본 개념은 특화면 컴퓨터 언어(specialized computer language)에서 각 심볼(terminal 또는 nonterminal)에 대한 클래스(class)를 가지는 것이다. 언어의 어떤 문장에 대한 문맥 트리(syntax tree)는 composite 패턴의 인스턴스이며, 그 문장을 평가(해석)하기 위해 사용된다. 


인터프리터 패턴의 사용 Uses for the Interpreter pattern

  • SQL같은 특화된(specialized) 데이터베이스 쿼리 언어들
  • 주로 통신 프로토콜(communication protocols)을 설명하기 위해 사용되는 특화된 컴퓨터 언어들
  • 대부분의 다목적(general-purpose) 컴퓨터 언어들은 실제로 몇몇 특화된 언어들을 포함한다.


구조 Structure



예 Example

아래의 Reverse Polish notation 예는 interpreter 패턴을 묘사하고 있다.

expression ::= plus | minus | variable | number
plus ::= expression expression '+'
minus ::= expression expression '-'
variable  ::= 'a' | 'b' | 'c' | ... | 'z'
digit = '0' | '1' | ... '9'
number ::= digit | digit number

위의 문법은 아래의 reverse Polish 표현법을 포함하는 언어를 정의한다.


a b +
a b c + -
a b + c a - -



아래의 interpreter 패턴은 각 문법 법칙에 대한 클래스이다.




interpreter 패턴은 parsing에 대해 언급하지 않지만 완벽함을 위해서 파서(parser)를 제공한다.



마지막으로 w = 5, x = 10, z = 42 일 때 "w x z - +"라는 표현식을 평가한다.


저작자 표시 비영리 변경 금지
신고

'Design Patterns > 영문 위키(Wikipedia)' 카테고리의 다른 글

Mediator pattern  (0) 2012.06.19
Iterator pattern  (0) 2012.06.18
Interpreter pattern  (0) 2012.06.15
Command Pattern  (0) 2012.06.15
Chain-of-responsibility pattern  (0) 2012.06.08
Proxy pattern (프록시 패턴)  (0) 2012.06.06
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/Command_pattern



Behavioral pattern - Command Pattern



객체 지향 프로그래밍(object-oriented programming)에서 command pattern(이하 커맨드 패턴)은 나중에 호출할 메서드를 위한 모든 정보를 대표(represent)하고 캡슐화(encapsulate)하는 디자인 패턴(design pattern)이다.

이 정보에는 메서드 이름, 메서드를 소유하고 있는 객체 그리고 메서드 인자에 필요한 값들을 포함하고 있다.


커맨드 패턴과 항상 연관되는 세가지 조건은 클라이언트(client), 호출자(invoker), 수신자(receiver)이다. 클라이언트(client)는 커맨드 객체를 인스턴스화하고, 나중에 메서드를 호출하는데 필요한 정보들을 제공한다. 호출자(invoker)는 메서드가 언제 호출되어야하는지 결정한다. 수신자(receiver)는 메서드 코드를 포함하고 있는 클래스의 객체이다.


커맨드 객체를 사용하는 것은 한 메서드를 어떤 클래스가 가지고 있든지 메서드의 인자가 무엇인지 알 필요없이 그 메서드를 원하는 시점에 위임하거나 호출할 필요가 있는 일반적인 컴포넌트를 만들기 쉽게 해준다.

사용 Uses

커맨드 객체는 다음과 같이 구현하기에 용이하다.


다단계 취소 Multi-level undo

만약 어떤 프로그램의 모든 사용자 액션이 커맨드 객체로 구현되어 있다면, 그 프로그램은 가장 최근에 실행한 명령들에 대한 스택(stack)을 유지할 수 있다. 사용자가 어떤 명령을 취소하고 싶다면, 그 프로그램은 그냥 가장 최근의 명령을 스택에서 뽑아내고(pop) undo()메서드를 호출하면 된다.


Transactional behavior

undo와 비슷하게, 데이터베이스 엔진이나 소프트웨어 설치 프로그램은 이미 수행한 오퍼레이션의 리스트나 앞으로 수행할 오퍼레이션의 리스트를 유지할 필요가 있을지도 모른다. 만약 그 오퍼레이션 중 하나라도 실패하게 된다면 나머지 모든 오퍼레이션도 취소될 수 있다.(일반적으로 rollback이라 부른다.) 예를들면, 두개의 데이터베이스 테이블이 있고 이 둘 중 하나가 수정되면 나머지 하나도 업데이트해야하는 관계일 때, 두번째 테이블의 업데이트가 실패하면 트랜젝션이 롤백(rolled back)되고, 첫번째 테이블은 무효한 레퍼런스를 가지고 있지 않는다.


Progress bars

어떤 프로그램이 일련의 명령들을 가지고 있고, 이 명령이 차례대로 수행된다고 가정해보자. 만약 각 명령 객체가 getEstimatedDuration()메서드를 가지고 있다면, 그 프로그램은 전체 수행 시간을 쉽게 추정할 수 있을 것이다. 이를 이용해서 프로그램이 모든 태스크 중 얼마나 완료했는지를 보여줄 수 있는 프로그래스 바에 적용할 수 있다.


Wizards

종종 wizard는 사용자가 마지막 페이지에 있는 "완료"버튼을 누르면 하나의 액션이 수행되는 여러 페이지의 설정을 나타낸다. 이러한 경우에 애플리케이션의 코드로부터 사용자 인터페이스 코드를 분리하는 자연스러운 방법은 커맨드 객체를 이용해서 wizard를 구현하는 것이다. 커맨드 객체는 wizard가 처음 화면에 나타낼 때 만들어진다. 각 wizard 페이지는 GUI의 변화를 커맨드 객체에 저장해서, 이 깨체가 사용자 진행상태로 이주된다.(populate, 원래는 거주하다라는 뜻인데,, 여기에서는 이 커맨드 객체가 사용자의 진행상태를 가지고 있게 된다 정도로 이해하면 될 것 같습니다.) "완료"는 단순히 execute()를 호출하도록 한다. 이 방법으로 커맨드 클래스는 일체의 사용자 인터페이스 코드를 가지지 않게 된다.


GUI 버튼과 메뉴 항목 GUI buttons and menu items

스윙(Swing)Borland Delphi 프로그래밍에서 Action은 커맨드 객체이다. 요구되어지는 명령을 수행하는 것 외에도 Action은 관련된 아이콘, 키보드 단축키, 툴팁 글자 등을 가질 수도 있다. 툴바 버튼이나 메뉴 항목 컴포넌트는 Action 객체를 이용해서 완벽하게 초기화될 수도 있다.


쓰레드 풀 Thread Pools

전형적으로, 일반적인 목적의 쓰레드 풀 클래스는 public 메서드인 addTask()를 가지고 있을지도 모른다. 이 메서드는 내부 큐(queue)에 작업 아이템을 추가하는 메서드이다. 이는 큐에 있는 명령을 수행하는 쓰레드의 풀을 유지한다. 큐에 있는 아이템은 커맨드 객체이다. 전형적으로 이런 객체들은 java.lang.Runnable같은 공통 인터페이스를 구현하는데, 이는 쓰레드 풀이 어떤 태스크를 수행할지에 대해 전혀 알지 못해도 된다.


Macro recording

만약 모든 사용자 액션이 커맨드 객체에 의해 표현된다면, 프로그램은 커맨드 객체가 수행되는대로 그 리스트를 유지함으로써 간단하게 일련의 액션들을 저장할 수 있다. 그리고나서 같은 커맨드 객체들을 차례대로 수행함으로써 같은 액션을 "다시 재생(play back)"할 수 있다. 만약 프로그램이 스크립팅 엔진을 사용하고 있다면, 각 커맨드 객체는 toScript()메서드를 구현할 수 있고, 사용자 액션은 스크립트로써 간단히 저장될 수 있다.


네트워킹 Networking

전체 커맨드 객체를 네트워크를 통해서 다른 머신에서 수행되도록 하는 것이 가능하다. 예를들면 컴퓨터 게임에서 플레이어의 액션을 볼 수 있다.


병렬 처리 Parallel Processing

명령이 공유 자원에 태스크로 쓰여지고, 많은 쓰레드에 의해 병렬적으로 수행되는 경우( 원격 머신의 경우 - 이 변형은 종종 Master/Worker 패턴으로 언급된다.)


Mobile Code

URLClassLoader와 Codebase를 통해서 한 곳에서 다른 한 곳으로 코드를 스트리밍할 수 있는 Java같은 언어를 사용함으로써, 명령이 원격지에 전송되도록 할 수 있다.(EJB Command, Master Worker)

구조 Structure


  • 위의 Receiver에 대한 설명은 "커맨드에 의해 수행되는 실질적인 작업"이라 할 수 있다.

전문 용어 Terminology

커맨드 패턴 구현을 설명하기 위한 용어는 일관적이지 않아서 혼란스러울 수도 있다. 이는 모호함(ambituity), 유의어(synonyms)의 사용, 원래 패턴에 대한 불분명할 수 있는 구현이라는 결과로 나타난다.

  1. 모호함(Ambiguity)
    1. command라는 용어는 모호하다. 예를들어 move up, move up은 2번 수행되는 하나의 (move up) 명령일 수도 있고, 같은 일(move up)을 하는  두 개의 (다른)커맨드를 의미할 수도 있다. 만약 첫번째 경우라고 할때 명령이 undo 스택에 두번 들어가면, 스택에 있는 두 아이템은 같은 커맨드 객체를 가리킨다. 이는 어떤 명령이 항상 같은 방법으로(여기에서는 예를들면 move down) 취소된다고하면 적절할 것이다. Gang of Four와 아래의 자바 예에서는 커맨드라는 용어를 이와 같은 방법으로 이해하고 있다. 반면에 후자의 경우에 undo 스택에 커맨드를 추가하면 두개의 다른 객체가 들어가게 될 것이다. 이는 스택에 있는 각각의 객체들이 명령이 취소되는데 필요한 정보를 가지고 있는 경우에 적절할 것이다. 예를 들어, "선택 영역 삭제"라는 명령을 취소하기 위해서 그 객체는 삭제된 텍스트의 사본을 가지고 있을 수도 있다. 그래서 "선택 영역 삭제"가 취소되어야 할 때 그 텍스트가 다시 추가될 수 있는 것이다. 어떤 명령의 각 호출에 대해 다른 객체를 사용하는 것은 chain of responsibility pattern의 한 예임을 알아두어라.
    2. execute라는 용어 또한 모호하다. 이는 command 객체의 excute 메서드에 의해 특정 코드를 수행하는 것을 의미할 수도 있다. 하지만, 마이크로소프트의 WPF(Windows Presentation Foundation) 에서 커맨드는 excute메서드가 호출됐을 때 커맨드가 수행됐음을 의미하지만, 이것이 애플리케이션 코드가 수행되었음을 필연적으로 의미하는 것은 아니다. 그는 약간의 이벤트 처리 후에만 발생한다.
  2. 유의어와 동음 이의어(Synonyms and homonyms)
    1. Client, Source, Invoker : 사용자에 의한 버튼, 툴바 버튼, 메뉴 항목 클릭, 단축키가 눌러짐.
    2. Command Object, Routed Command Object, Action Object : command와 관련있는 단축키, 버튼 이미지, 커맨드 텍스트 등에 대해 알고있는 싱글톤 객체. source/invoker 객체는 Command/Action 객체의 excute/performAction 메서드를 호출한다. Command/Action 객체는 command/action의 유용성(availability)이 바뀌었을 때 적절한 source/invoker 객체에게 알려준다. 이는 command/action이 excute/perform할 수 없을 때 버튼과 메뉴 항목을 비활성화(회색으로)시킬 수 있도록 한다.
    3. Receiver, Target Object : 복사, 붙여넣기, 또는 움질일 객체. receiver 객체는 command의 excute 메서드에 의해 호출되는 메서드를 가지고 있다. receiver는 전형적으로 target 객체이기도 하다. 예를들어, receiver 객체가 cursor이고, 이 객체의 메서드가 moveUp이라면, cursor는 moveUp 액션의 target이라고 생각될 수 있을 것이다. 반면에, 만약 코드가 command 객체 자신에 의해 정의되어진다면 target 객체는 완전히 다른 객체일 것이다. 
    4. Command 객체, routed event 인자들, event 객체 : 소스(source)로부터 Command/Action 객체로, Target 객체로, 그것이 동작하는 코드로 전달되는 객체. 각 버튼 클릭이나 단축키는 새로운 command / event객체에 새로운 결과를 야기한다. 일부 구현에서는 CopyCommand같은 객체로부터 문서 영역(document section)같은 객체로 전달되기 때문에 command/event 객체에 더 많은 정보를 추가한다. 다른 구현에서는 네이밍 충돌(naming conflicts)를 피하기 위해 데이터를 한줄로 이동시키기때문에 command / event 객체를 다른 event 객체에 넣어둔다.(큰 박스 안에 작은 박스가 있는 것처럼. chain of responsibility pattern을 보라.)
    5. Handler, ExecutedRoutedEventHandler, method, function : 복사, 붙여넣기, 옮기기 등을 실제로하는 코드. 일부 구현에서 handler코드는 command/action 객체의 일부이다. 다른 구현에서는 Receiver/Target객체의 일부이고, 또 다른 구현에서 handler코드는 다른 객체로부터 분리되어진다. 
    6. Command Manager, Undo Manager, Scheduler, Queue, Dispatcher, Invoker : command / event객체를 undo 스택이나 redo스택에 넣는 객체. 또는 다른 객체가 command/event객체에 대해서 어떤 액션을 취할 준비가 될때까지 command / event 객체에 유지하고 있는 객체. 또는 command / event객체를 적절한 receiver/target 객체나 handler 코드에 보내는 객체.
  3. 원래 command 패턴을 뛰어넘는 구현
    1. routed 커맨드를 도입하고 있는 마이크로소프트의 Windows Presentation Foundation (WPF)는 command 패턴과 event 처리를 합쳐놓았다. 그 결과 command 객체는 더 이상 target객체에 대한 레퍼런스 뿐만 아니라 애플리케이션 코드에 대한 레퍼런스도 가지고 있지 않다. 대신에 command 객체의 execute 메서드를 호출하는 것은 소위 말하는 Executed Routed Event가 호출되는 결과를 야기한다. 이 때 Executed Routed Event 는 event의 터널링(tunneling)또는 버블링(bubbling)을 하는 동안 binding과 직면할 수 있는데, binding은 target과 애플리케이션 코드를 확실히 해준다.



예 Example

"간단한" 스위치를 생각해보자. 이 예에서 Switch는 2개의 command를 가지고 있다. (불을 켜는 것과 불을 끄는 것)

이런 command패턴의 구현의 이점은 스위치가 어떤 기기에 상관없이 사용될 수 있다는 것이다. (불(light)뿐만 아니라 다른 것과 관련해서도 사용될 수 있다.) - 이 예에서는 불을 켜고 끄는 스위치이지만, Switch의 생성자에서는 Command의 어떤 서브클래스든지 받을 수 있다. 예를들어, 엔진에 시동을 거는 스위치를 만들 수도 있다. 


아래의 샘플 애플리케이션은 실제 전기 회로의 모델화한 것은 아니라는 점을 확실히하자. 전기적인 스위치는 벙어리다(dumb, 바보 정도로 해석해도 될 것 같네요). 실제 이진 스위치는 붙었는지 떨어졌는지 여부만 알 수 있다. 스위치는 회로에 연결되어 있을 수 있는 어떤 직접적인 부하(loads)와의 관계에 대해 알지도 못하고 가지지도 못한다. 스위치는 단순히 현재 상태에 대한 이벤트만 알릴 뿐이다. (ON 또는  OFF). 그러면 회로는 상태 엔진(State Engine)을 가지고 있어야 하는데, 이 상태엔진은 다양한 시점에서 적절한 많은 부하와 스위치가 있는 복잡한 회로를 수용하기 위해 회로의 상태를 관리한다. 그리고 각 부하는 특정 회로이 상태에 대한 조건이다. 결론을 내리자면 스위치는 어떤 램프의 자세한 내용에 대해서도 알지 못한다. 


Java


Python

class Switch(object):
    """The INVOKER class"""
    def __init__(self, flip_up_cmd, flip_down_cmd):
        self.flip_up = flip_up_cmd
        self.flip_down = flip_down_cmd
 
class Light(object):
    """The RECEIVER class"""
    def turn_on(self):
        print "The light is on"
    def turn_off(self):
        print "The light is off"
 
class LightSwitch(object):
    """The CLIENT class"""
    def __init__(self):
        lamp = Light()
        self._switch = Switch(lamp.turn_on, lamp.turn_off)
 
    def switch(self, cmd):
        cmd = cmd.strip().upper()
        if cmd == "ON":
            self._switch.flip_up()
        elif cmd == "OFF":
            self._switch.flip_down()
        else:
            print 'Argument "ON" or "OFF" is required.'
 
# Execute if this file is run as a script and not imported as a module
if __name__ == "__main__":
    light_switch = LightSwitch()
    print "Switch ON test."
    light_switch.switch("ON")
    print "Switch OFF test."
    light_switch.switch("OFF")
    print "Invalid Command test."
    light_switch.switch("****")


C#


저작자 표시 비영리 변경 금지
신고

'Design Patterns > 영문 위키(Wikipedia)' 카테고리의 다른 글

Iterator pattern  (0) 2012.06.18
Interpreter pattern  (0) 2012.06.15
Command Pattern  (0) 2012.06.15
Chain-of-responsibility pattern  (0) 2012.06.08
Proxy pattern (프록시 패턴)  (0) 2012.06.06
Flyweight pattern (플라이웨이트 패턴)  (0) 2012.06.05
Posted by Code-Moon

댓글을 달아 주세요

먼저 이 글은 영문 위키의 글을 번역한 글임을 알려드립니다.

영어 실력이 부족한 관계로 오역이 있을 수도 있습니다.

원문 주소 : http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern



행위 패턴 - Chain-of-responsibility




객체지향 프로그래밍(Object Oriented Design)에서 chain-of-responsibility 패턴command objects의 소스와 일련의 processing objects로 구성되는 디자인 패턴이다. 각각의 processing object는 그것이 다룰 수 있는 command objects의 타입을 정의하는 로직을 포함한다.; 나머지 것들은 체인(chain)의 다음processing object에게 전달된다. 메카니즘은 체인의 마지막에 새로운 processing objects를 추가하는 것에도 존재한다. 


기본 chain-of-responsibility 모델의 변경에서는, 일부 핸들러(handlers)가 dispatchers의 역할을 할 수도 있다. 여기에서 dispatcher는 책임의 나무(tree of responsibility)의 형태를 보이게끔 다양한 방향으로 명령들을(commands) 보낼 수 있다. 몇몇 상황에서는 이런 일이 재귀적으로 발생할 수 있는데, processing objects가 더 높은 레벨의(higher-up) processing objects를 호출하고 이 때의 commands는 문제의 더 작은 일부분을 풀기위한 시도이다.; 이런 상황에서 반복(recursion)은 command가 처리될때까지 계속되거나, 전체 트리(tree)를 다 돌았을때(explored, 탐험했을때) 끝난다. XML 인터프리터는 아마도 이러한 방법으로 동작할 것이다.


이 패턴은 programming best practice의 하나로 간주되는 느슨한 결합(lose coupling)의 아이디어에서 나온 것이다.

예 Example

아래의 코드는 로그를 기록하는 클래스를 예로 들어 이 패턴에 대해 설명한다. 각각의 로그 핸들러는 현재 로그 레벨에서 어떤 행동(action)을 취할지 결정하고 다음 로그 핸들러에게 메세지를 전달한다. 이 예는 어떻게 로그 클래스를 작성하는지에 대한 추천하는 것은 아니지 주의해라.


또한 chain of responsiblity 패턴의 순수한('pure') 구현에서 로거(logger)는 메세지를 처리하고 난 뒤에 다음 체인으로 책임을 전달하지 않을지도 모른다는 것을 알아둬라. 이 예에서는 처리가 되었든 안되었든 상관안하고 메세지는 체인으로 전달될 것이다.

Java

package chainofresp;
 
abstract class Logger {
    public static int ERR = 3;
    public static int NOTICE = 5;
    public static int DEBUG = 7;
    protected int mask;
 
    // The next element in the chain of responsibility
    protected Logger next;
 
    public Logger setNext(Logger log) {
        next = log;
        return log;
    }
 
    public void message(String msg, int priority) {
        if (priority <= mask) {
            writeMessage(msg);
        }
        if (next != null) {
            next.message(msg, priority);
        }
    }
 
    abstract protected void writeMessage(String msg);
}
 
class StdoutLogger extends Logger {
    public StdoutLogger(int mask) { 
        this.mask = mask;
    }
 
    protected void writeMessage(String msg) {
        System.out.println("Writing to stdout: " + msg);
    }
}
 
 
class EmailLogger extends Logger {
    public EmailLogger(int mask) {
        this.mask = mask;
    }
 
    protected void writeMessage(String msg) {
        System.out.println("Sending via email: " + msg);
    }
}
 
class StderrLogger extends Logger {
    public StderrLogger(int mask) {
        this.mask = mask;
    }
 
    protected void writeMessage(String msg) {
        System.err.println("Sending to stderr: " + msg);
    }
}
 
public class ChainOfResponsibilityExample {
    public static void main(String[] args) {
        // Build the chain of responsibility
        Logger logger, logger1,logger2;
        logger = new StdoutLogger(Logger.DEBUG);
        logger1 = logger.setNext(new EmailLogger(Logger.NOTICE));
        logger2 = logger1.setNext(new StderrLogger(Logger.ERR));
 
        // Handled by StdoutLogger
        logger.message("Entering function y.", Logger.DEBUG);
 
        // Handled by StdoutLogger and EmailLogger
        logger.message("Step1 completed.", Logger.NOTICE);
 
        // Handled by all three loggers
        logger.message("An error has occurred.", Logger.ERR);
    }
}
/*
The output is:
   Writing to stdout:   Entering function y.
   Writing to stdout:   Step1 completed.
   Sending via e-mail:  Step1 completed.
   Writing to stdout:   An error has occurred.
   Sending via e-mail:  An error has occurred.
   Writing to stderr:   An error has occurred.
*/


C#

using System;
using System.IO;
 
namespace ChainOfResponsibility
{
    public enum LogLevel
    {
        Info=1,
        Debug=2,
        Warning=4,
        Error=8,
        FunctionalMessage=16,
        FunctionalError=32,
        All = 63
    }
 
    /// 
    /// Abstract Handler in chain of responsibility Pattern
    /// 
    public abstract class Logger
    {
        protected LogLevel logMask;
 
        // The next Handler in the chain
        protected Logger next;
 
        public Logger(LogLevel mask)
        {
            this.logMask = mask;
        }
 
        /// 
        /// Sets the Next logger to make a list/chain of Handlers
        /// 
        public Logger SetNext(Logger nextlogger)
        {
            next = nextlogger;
            return nextlogger;
        }
 
        public void Message(string msg, LogLevel severity)
        {
            if ((severity & logMask) != 0)
            {
                WriteMessage(msg);
            }
            if (next != null)
            {
                next.Message(msg, severity);
            }
        }
 
        abstract protected void WriteMessage(string msg);
    }
 
    public class ConsoleLogger : Logger
    {
        public ConsoleLogger(LogLevel mask)
            : base(mask)
        { }
 
        protected override void WriteMessage(string msg)
        {
            Console.WriteLine("Writing to console: " + msg);
        }
    }
 
    public class EmailLogger : Logger
    {
        public EmailLogger(LogLevel mask)
            : base(mask)
        { }
 
        protected override void WriteMessage(string msg)
        {
            //Placeholder for mail send logic, usually the email configurations are saved in config file.
            Console.WriteLine("Sending via email: " + msg);
        }
    }
 
    class FileLogger : Logger
    {
        public FileLogger(LogLevel mask)
            : base(mask)
        { }
 
        protected override void WriteMessage(string msg)
        {
            //Placeholder for File writing logic
            Console.WriteLine("Writing to Log File: " + msg);
        }
    }
 
    public class Program
    {
        public static void Main(string[] args)
        {
            // Build the chain of responsibility
            Logger logger, logger1, logger2;
            logger1 = logger = new ConsoleLogger(LogLevel.All);
            logger1 = logger1.SetNext(new EmailLogger(LogLevel.FunctionalMessage | LogLevel.FunctionalError));
            logger2 = logger1.SetNext(new FileLogger(LogLevel.Warning | LogLevel.Error));
 
            // Handled by ConsoleLogger
            logger.Message("Entering function ProcessOrder().", LogLevel.Debug);
            logger.Message("Order record retrieved.", LogLevel.Info);
 
            // Handled by ConsoleLogger and FileLogger
            logger.Message("Customer Address details missing in Branch DataBase.", LogLevel.Warning);
            logger.Message("Customer Address details missing in Organization DataBase.", LogLevel.Error);
 
            // Handled by ConsoleLogger and EmailLogger
            logger.Message("Unable to Process Order ORD1 Dated D1 For Customer C1.", LogLevel.FunctionalError);
 
            // Handled by ConsoleLogger and EmailLogger
            logger.Message("Order Dispatched.", LogLevel.FunctionalMessage);
        }
    }
}
 
/* Output
Writing to console: Entering function ProcessOrder().
Writing to console: Order record retrieved.
Writing to console: Customer Address details missing in Branch DataBase.
Writing to Log File: Customer Address details missing in Branch DataBase.
Writing to console: Customer Address details missing in Organization DataBase.
Writing to Log File: Customer Address details missing in Organization DataBase.
Writing to console: Unable to Process Order ORD1 Dated D1 For Customer C1.
Sending via email: Unable to Process Order ORD1 Dated D1 For Customer C1.
Writing to console: Order Dispatched.
Sending via email: Order Dispatched.
*/

또 다른 예 Another Example

아래는 자바로 작성한 또 다른 예이다. 이 예에서 우리는 다른 역할들을 가지고 있다. 각각의 역할들은 정해진 구매 한계치(purchasing limit)와 하나의 후계자(successor)를 가진다.(여기에서 후계자는 앞에서 나왔던 chain이라고 생각하시면 됩니다. 책임을 전달한 객체를 의미하죠.) 역할을 담당해야하는 유저는 구매 요청을 받는데 이 구매 요청이 그의 한계를 초과하는 경우에, 요청은 그의 후계자로 전달된다.


processRequest 추상 메서드를 가지고 있는 PurchasePower 추상 클래스

abstract class PurchasePower {
    protected final double base = 500;
    protected PurchasePower successor;
 
    public void setSuccessor(PurchasePower successor) {
        this.successor = successor;
    }
 
    abstract public void processRequest(PurchaseRequest request);
}


위의 추상 클래스를 상속받는 4개의 구현들 : Manager, Director, Vice President, President

class ManagerPPower extends PurchasePower {
    private final double ALLOWABLE = 10 * base;
 
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("Manager will approve $" + request.getAmount());
        } else if (successor != null) {
            successor.processRequest(request);
        }
    }
}
 
class DirectorPPower extends PurchasePower {
    private final double ALLOWABLE = 20 * base;
 
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("Director will approve $" + request.getAmount());
        } else if (successor != null) {
            successor.processRequest(request);
        }
    }
}
 
class VicePresidentPPower extends PurchasePower {
    private final double ALLOWABLE = 40 * base;
 
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("Vice President will approve $" + request.getAmount());
        } else if (successor != null) {
            successor.processRequest(request);
        }
    }
}
 
class PresidentPPower extends PurchasePower {
    private final double ALLOWABLE = 60 * base;
 
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("President will approve $" + request.getAmount());
        } else {
            System.out.println( "Your request for $" + request.getAmount() + " needs a board meeting!");
        }
    }
}


아래의 코드는 이 예에서 요청 데이터를 유지하는 PurchaseRequest 클래스를 정의한다.

class PurchaseRequest {
    private int number;
    private double amount;
    private String purpose;
 
    public PurchaseRequest(int number, double amount, String purpose) {
        this.number = number;
        this.amount = amount;
        this.purpose = purpose;
    }
 
    public double getAmount() {
        return amount;
    }
    public void setAmount(double amt)  {
        amount = amt;
    }
 
    public String getPurpose() {
        return purpose;
    }
    public void setPurpose(String reason) {
        purpose = reason;
    }
 
    public int getNumber(){
        return number;
    }
    public void setNumber(int num) {
        number = num;
    }   
}


아래의 사용 예에서 후계자들(successors)은 뒤에 나오는 순서대로이다. : Manager->Director->Vice President->President

class CheckAuthority {
    public static void main(String[] args) {
        ManagerPPower manager = new ManagerPPower();
        DirectorPPower director = new DirectorPPower();
        VicePresidentPPower vp = new VicePresidentPPower();
        PresidentPPower president = new PresidentPPower();
        manager.setSuccessor(director);
        director.setSuccessor(vp);
        vp.setSuccessor(president);
 
        // Press Ctrl+C to end.
        try {
            while (true) {
                System.out.println("Enter the amount to check who should approve your expenditure.");
                System.out.print(">");
                double d = Double.parseDouble(new BufferedReader(new InputStreamReader(System.in)).readLine());
                manager.processRequest(new PurchaseRequest(0, d, "General"));
           }
        } catch(Exception e) {
            System.exit(1);
        }  
    }
}


저작자 표시 비영리 변경 금지
신고

'Design Patterns > 영문 위키(Wikipedia)' 카테고리의 다른 글

Interpreter pattern  (0) 2012.06.15
Command Pattern  (0) 2012.06.15
Chain-of-responsibility pattern  (0) 2012.06.08
Proxy pattern (프록시 패턴)  (0) 2012.06.06
Flyweight pattern (플라이웨이트 패턴)  (0) 2012.06.05
Facade pattern (퍼사드 패턴)  (0) 2012.06.04
Posted by Code-Moon

댓글을 달아 주세요

티스토리 툴바