본문 바로가기

Design Patterns/영문 위키(Wikipedia)

State pattern

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

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

원문 주소 : 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
Observer pattern  (0) 2012.06.27
Memento pattern  (0) 2012.06.20
Mediator pattern  (0) 2012.06.19