Featured image of post 10. 객체의 상태 바꾸기 - 상태 패턴

10. 객체의 상태 바꾸기 - 상태 패턴

헤드 퍼스트 디자인 패턴

전략 패턴과 상태 패턴은 쌍둥이이다.
전략 패턴은 알고리즘을 바꾸어 사용하는 반면 상태 패턴은 내부 상태를 바꿈으로써 객체가 행동을 바꿀 수 있도록 도와준다.

상태 기계

상태 기계(State Machine)는 객체나 시스템이 다양한 상태를 가지고 있고, 특정 이벤트에 의해 이 상태가 변하는 시스템을 나타내는 모델이다.

상태 기계는 상태, 이벤트, 전이(Transition), 동작(Action) 등의 개념을 포함하며, 객체나 시스템이 특정 상태에서 다른 상태로 전환되는 것을 관리한다.

  • 상태(State): 시스템이나 객체가 가질 수 있는 다양한 상태를 나타낸다.
    • 각 상태는 특정 시점에서 시스템이 어떤 동작을 수행하고 있는지를 표현한다.
  • 이벤트(Event): 상태 전이를 유발하는 외부나 내부에서 발생하는 사건이나 신호를 나타낸다.
    • 이벤트는 특정 상태에서만 발생하거나 처리될 수 있다.
  • 전이(Transition): 상태 간 전환을 정의한다.
    • 특정 상태에서 특정 이벤트가 발생하면 어떤 다음 상태로 전환되어야 하는지를 정의한다.
  • 동작(Action): 상태 전이가 발생할 때 수행되는 특정 동작이나 처리를 나타낸다.
    • 각 전이에는 연관된 동작이 정의될 수 있다.

상태 기계는 주로 시스템의 복잡한 동작을 모델링하고 이해하기 쉽게 만들기 위해 사용된다.

소프트웨어 개발에서는 상태 기계를 사용하여 객체의 동작을 상태에 따라 효과적으로 제어할 수 있다.

상태 기계는 **유한 상태 기계(Finite State Machine, FSM)**로 불리기도 하며, 이는 상태의 수가 유한하다는 특성을 강조한 용어이다.

뽑기 기계 상태 다이어그램

flowchart LR
    s([시작])
    a((동전 있음))
    b((동전 없음))
    c((알맹이 판매))
    d((알맹이 매진))
    e{알맹이 개수 > 0}
    
    s-->a
    a-- 동전 반환 -->b
    b-- 동전 투입 -->a
    a-- 손잡이 돌림 --> c
    c-- 알맹이 내보냄 -->e
    e-- Y -->b
    e-- N --> d
  • 동전 있음, 없음, 알맹이 판매, 매진이 상태
  • 손잡이 돌임, 동전 투입, 알맹이 내보냄, 동전 반환 행동을 인터페이스라고 할 수 있음
  • 행동들을 실행할 때 상태가 변경됨
  • 알맹이를 꺼내는 행동은 기계 내에서 자체적으로 진행하는 행동에 가까움

상태 패턴

상태 패턴을 사용하면 객체의 내부 상태가 바뀜에 따라 객체의 행동을 바꿀 수 있다.
“마치 객체의 클래스가 바뀌는 것과 같이”

상태 패턴은 상태를 별도의 클래스로 캡슐화한 다음 현재 상태를 나타내는 객체에게 행동을 위임하므로 내부 상태가 바뀔 때 행동이 달라진다.

또한 클래스가 구성으로 여러 상태 객체를 바꿔가며 사용하게되므로, 클라이언트 관점에서 현재 사용하는 객체의 행동이 완전히 달라져 마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있다.

classDiagram
    class Context {
        request()
    }
    
    class State {
        << interface >>
        handle()*
    }
    
    class ConcreteStateA {
        handle()
    }

    class ConcreteStateB {
        handle()
    }
    
    Context --> State
    State <|-- ConcreteStateA
    State <|-- ConcreteStateB
  • Context: request()를 통해 상태의 handle()을 호출한다.
  • State: 모든 구상 상태 클래스의 공통 인터페이스를 정의한다.
    • 모든 상태 클래스에서 같은 인터페이스를 구현하므로 바뀌 가면서 사용할 수 있다.
  • ConcreteState: Context로 부터 전달된 요청을 자기 나름의 방식으로 구현하여 처리한다.
    • 이를 통해 Context에서 상태를 바꿀 때마다 행동도 바뀌게 된다.

상태 패턴은 객체의 내부 상태에 따라 수행되는 객체의 행위를 상태 객체로 캡슐화하여 객체의 상태를 표현하고 있는 클래스를 정의하고 상태에 따라 객체의 행동을 변경할 수 있도록 한다.

이를 통해 객체의 상태를 변경할 때마다 직접 조건문이다 switch문을 사용하는 대신, 상태에게 해당 행동을 위임하여 코드의 유지보수성과 확장성을 향상시킨다.

특징

장점

  • 새로운 상태를 추가하거나 상태를 변경할 때 기존 코드를 건드리지 않고 확장이 가능하다.
  • 상태와 관련된 코드가 상태 객체에 캡슐화되어 있어 코드가 더 간결하고 읽기 쉬워진다.
  • 상태 전환 로직이 각 상태에 캡슐화 되어있어 유지보수가 용이하다.

단점

  • 상태가 많을 경우 클래스의 수가 급격히 증가할 수 있고, 상태 간의 전이 로직이 복잡해질 수 있다.

전략 패턴과의 차이점

전략 패턴과 상태 패턴 모두 객체 간의 알고리즘을 정의하고, 이를 동적으로 교환할 수 있게 하는 구조적 디자인 패턴이지만, 목적과 사용 시나리오에서 차이가 있다.

  1. 목적
  • 전략 패턴
    • 알고리즘의 변형이나 여러 알고리즘 중 하나를 선택해야할 때 사용
    • 주로 알고리즘이나 전략 간의 상호 교환이 필요한 경우 적합
  • 상태 패턴
    • 객체의 내부 상태에 따라 행동이 달라져야 할 때 사용
    • 객체가 상태에 따라 직접적으로 행동을 변경해야 하는 경우 유용함
  1. 관리 대상
  • 전략 패턴
    • 알고리즘, 전략, 행동을 캡슐화
    • 객체가 특정 행동을 수행하는 데 사용되는 알고리즘을 변경하고 싶을 때 적용
  • 상태 패턴
    • 객체의 내부 상태를 캡슐화
    • 객체가 내부 상태에 따라 행동을 변경하고 싶을 때 적용
  1. 구조적 차이
  • 전략 패턴
    • ContextStrategy 인터페이스를 가지며, 여러 ConreteStrategy를 가진다.
    • Context는 전략을 동적으로 교환할 수 있다.
  • 상태 패턴
    • ContextState 인터페이스를 가지며, 여러 ConcreteState를 가진다.
    • Context는 전략을 동적으로 교환할 수 있다.
  1. 전이
  • 전략 패턴
    • 전략이 변경되면 Context는 완전히 다른 알고리즘으로 전환된다.
  • 상태 패턴
    • 상태가 변경되면 Context의 행동이 변경되지만, 전체 시스템이 완전히 다른 상태로 이동하는 것은 아니다.
  1. 예시
  • 전략 패턴
    • 정렬 알고리즘을 선택하는 경우
    • 결제 시스템에서 여러 결제 전략 중 하나를 선택하는 경우
  • 상태 패턴
    • 자동판매기의 동작을 모델링하는 경우
    • 객체가 다양한 상태에 있을 때 각 상태에 따라 다른 행동을 하는 경우

정리

  • 상태 패턴: 클래스에서 상태를 관리해야 한다면 상태 패턴을 써서 상태를 캡슐화 할 수 있다.
    • 내부 상태가 바뀜에 따라 객체의 행동이 바뀔 수 있도록 해준다
    • 마치 객체의 클래스가 바뀌는 것 같은 결과를 얻을 수 있다.

  • 상태 패턴을 사용하면 내부 상태를 바탕으로 여러 가지 서로 다른 행동을 사용할 수 있다.
  • 상태 패턴을 사용하면 프로시저형 상태 기계를 쓸 때와는 달리 각 상태를 클래스로 표현한다.
  • Context 객체는 형재 상태에게 행동을 위임한다.
  • 각 상태를 클래스로 캡슐화해서 나중에 변경해야 하는내용을 국지화할 수 있다.
  • 상태 패턴과 전략 패턴의 클래스 다이어그램은 똑같지만 용도는 다르다.
  • 전략 패턴에서 Context의 내부 상태가 바뀜에 따라 객체가 알아서 행동을 바꿀 수 있도록 할 수 있다.
  • 상태 전환은 State 클래스로 제어할 수도 있고, Context 클래스로 제어할 수도 있다.
  • State 클래스를 여러 Context 객체의 인스턴스에서 공유하도록 디자인할 수도 있다.