mojo's Blog

State Pattern 본문

Design Patterns

State Pattern

_mojo_ 2024. 6. 6. 16:01

State Pattern

 

목적: 객체 상황을 객체의 동작과 연결하여 객체가 내부 상태에 따라 다른 방식으로 동작할 수 있도록 함

사용 시기

- 객체의 행동은 객체의 상태에 의해 영향을 받아야 함

- 복잡한 조건은 객체 동작을 상태와 연결함

- 상태 간 전환은 명시적이여야 함

 

 

참고:  Eraser: State Diagrams 

위와 같은 상태 다이어그램을 코딩해보자

 

public class Eraser {
    final static int IDLE = 0;
    final static int AWAITING_INPUT = 1;
    final static int IN_PROGRESS = 2;
    final static int COMPLETE = 3;
    
    int state;
    
    public Eraser() {
    	state = AWAITING_INPUT;
    }
    public void begin() { 
    	if (state == IDLE) {
        	state = IN_PROGRESS;
            System.out.println("Idle => In Progress");
        }
        else if (state == AWAITING_INPUT)
        	System.out.println("Waited status");
        else if (state == IN_PROGRESS)
        	System.out.println("Progressed status");
        else if (state == COMPLETE)
        	System.out.println("Completed status");
    }
    public void reset() { // impl reset }
    ...
}

 

작업을 수행하는 메서드마다 if 문을 4개의 상태 별로 구현이 이뤄져야 한다.

코드의 가독성이 떨어지는 문제가 발생한다.

 

※ New Design (composition)

- Eraser의 모든 작업에 대한 메서드를 포함하는 상태 인터페이스를 정의함

- 시스템의 모든 상태에 대해 상태 클래스를 구현함

- 머신이 해당 상태에 있을 때 머신의 동작을 담당함

- 위와 같이 조건부 코드를 모두 제거하고 대신 작업을 수행하도록 주 객체에게 위임함

 

 

public class Eraser {
	State idleState;
    State awaitingInputState;
    State inProgressState;
    State completeState;
    
    State state;
    
    public Eraser() {
    	idleState = new IdleState(this);
        awaitingState = new AwaitingState(this);
        inProgressState = new InProgressState(this);
        completeState = new CompleteState(this);
     	
        state = awaitingInputState;
    }
    public void begin() { 
    	state.begin();
    }
    public void reset() { 
    	state.reset();
    }
    ...
}

public class IdleState implements State {
	Eraser eraser;
    
    public IdleState(Eraser eraser) {
    	this.eraser = eraser;
    }
    public void begin() {
    	eraser.setState(eraser.getInProgressState());
        System.out.println("Idle => In Progress");
    }
    public void reset() {
    	System.out.println("Don't reset (Idle status)");
    }
    public void input() {
    	System.out.println("Don't input (Idle status)");
    }
    public void calculation() {
    	System.out.println("Don't calculation (Idle status)");
    }
    public void solution() {
    	System.out.println("Don't solution (Idle status)");
    }
}

 

원래 버전과 구조적으로 다르지만, 기능적으로는 동일하다.

변경 사항

- 각 상태의 동작을 고유 클래스로 현지화함

- 유지 관리가 어려울 것으로 예상되는 번거로운 조건문을 모두 제거 

- 수정을 위해 각 상태를 닫았지만, 새 상태 클래스를 추가하여 Eraser을 확장할 수 있도록 열어둠

- 상태 다이어그램에 훨씬 더 가깝고 매핑되고 읽기 쉽고 이해하기 쉬운 코드 베이스 및 클래스 구조를 만듬

 

※ The State Pattern

상태 패턴을 사용하면 객체의 내부 상태가 변경될 때 객체의 동작을 변경할 수 있다.

객체가 클래스를 변경하는 것으로 나타난다.

 

 

객체의 동작은 객체의 상태에 따라 다르며, 객체는 해당 상태에 따라 런타임에 동작을 변경해야 한다.

연산에는 객체의 상태에 따라 큰 다중 부분 조건문이 존재한다.

상태 패턴은 조건의 각 분기를 별도의 클래스에 배치함으로써 조건문을 없앤다.

 

※ State v.s. Strategy

State

- 상태 종속 동작을 캡슐화함

- 시간이 지남에 따라 컨텍스트의 동작이 바뀜

 

Strategy

- 알고리즘을 캡슐화함

- 종종 컨텍스트 객체에 가장 적합한 전략 객체가 있음

- 하위 분류에 대한 유연한 대안

 

두 패턴 전부 delegation을 활용한 composition 이다.

 

'Design Patterns' 카테고리의 다른 글

Factory Method Pattern & Abstract Factory Pattern  (0) 2024.06.09
Mediator Pattern  (0) 2024.06.06
Template Method Pattern  (0) 2024.06.06
Observer Pattern  (1) 2024.06.06
Strategy Pattern  (0) 2024.06.06
Comments