포스트

[Behavioral Pattern] 상태 패턴 (State Pattern)

Design Pattern / Behavioral Pattern

상태 패턴의 정의와 해당 디자인 패턴의 예제 코드를 통한 이해 및 설명 정리

개념

상태란? 객체가 가질 수 있는 어떤 조건이나 상황을 의미

상태 클래스는 싱글톤 클래스로 구성

  • 객체가 특정 상태에 따라 행위를 달리하는 상황에서 상태를 조건문으로 검사해서 행위를 달리하는 것이 아닌, 상태를 객체화하여 상태가 행동을 할 수 있도록 위임하는 패턴

  • 객체 지향 프로그래밍에서의 클래스는 꼭 사물/생물만을 표현하는 고체 형태의 데이터만 표현할 수 있는게 아닌, 경우에 따라서 무형태의 행위/동작도 클래스로 묶어 표현할 수 있음

    • 따라서 상태를 클래스로 표현하면 클래스를 교체해서 ‘상태의 변화’를 표현할 수 있고, 객체 내부 상태 변경에 따라 객체의 행동을 상태에 특화된 행동들로 분리해 낼 수 있으며, 새로운 행동을 추가하더라도 다른 행동에 영향을 주지 않음

패턴 구조

state

  • State 인터페이스

    • 상태를 추상화한 고수준 모듈
  • ConcreteState

    • 구체적인 각각의 상태를 클래스로 표현

    • State 역할로 결정되는 인터페이스(API)를 구체적으로 구현

    • 다음 상태가 결정되면 Context에 상태 변경을 요청하는 역할도 함

  • Context

    • State를 이용하는 시스템

    • 시스템 상태를 나타내는 State 객체를 합성(composition)하여 가지고 있음

    • 클라이언트로부터 요청받으면 State 객체에 행위 실행을 위임

예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class Context {
  private state?: State;

  constructor(state: State) {
    this.transitionTo(state);
  }

  public transitionTo(state: State): void {
    console.log(`Context: Transition to ${(<any>state).constructor.name}`);
    this.state = state;
    this.state.setContext(this);
  }

  public request_1(): void {
    this.state?.handle_1();
  }

  public request_2(): void {
    this.state?.handle_2();
  }
}

abstract class State {
  protected context?: Context;

  public setContext(context: Context) {
    this.context = context;
  }

  public abstract handle_1(): void;

  public abstract handle_2(): void;
}

class ConcreteState_A extends State {
  public handle_1(): void {
    console.log("ConcreteState_A handles request_1");
    console.log("ConcreteState_A wants to change the state of the context");
    this.context?.transitionTo(new ConcreteState_B());
  }

  public handle_2(): void {
    console.log("ConcreteState_A handles request_2");
  }
}

class ConcreteState_B extends State {
  public handle_1(): void {
    console.log("ConcreteState_B handles request_1");
  }

  public handle_2(): void {
    console.log("ConcreteState_B handles request_2");
    console.log("ConcreteState_B wants to change the state of the context");
    this.context?.transitionTo(new ConcreteState_A());
  }
}

const context = new Context(new ConcreteState_A());
context.request_1();
context.request_2();

참고한 출처 사이트

Refactoring GURU

Inpa Dev Blog (디자인 패턴)

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.