2 minute read

상태(State) 패턴이란?


상태 패턴은 객체의 내부 상태에 따라 객체의 행동이 달라지도록 하는 행위 디자인 패턴입니다. 객체의 상태를 별도의 클래스로 캡슐화하고, 객체의 상태가 변경될 때마다 현재 상태 객체를 교체함으로써 객체의 행동을 변경합니다.
마치 자동 판매기의 상태가 동전 투입 전, 동전 투입 후, 상품 선택 후 등으로 바뀌는 것처럼, 상태 패턴은 객체의 상태 변화에 따라 자연스럽게 행동이 바뀌도록 합니다.

상태(State) 패턴의 구성 요소


  • Context (컨텍스트): 현재 상태를 나타내는 객체에 대한 참조를 가지고 있는 객체입니다. 클라이언트가 사용하는 객체이며, 상태 객체에 작업을 위임합니다.
  • State (상태 인터페이스): 모든 구체적인 상태 클래스가 구현해야 하는 인터페이스 또는 추상 클래스입니다. 상태에 따른 행동을 정의하는 메서드를 선언합니다.
  • Concrete State (구체적인 상태): 실제 상태를 구현하는 클래스입니다. State 인터페이스를 구현하며, 특정 상태에서의 행동을 정의합니다.

상태(State) 패턴의 작동 방식


  1. Context 객체는 초기 상태 객체를 생성하여 가지고 있습니다.
  2. 클라이언트가 Context 객체의 특정 작업을 요청하면, Context 객체는 현재 상태 객체의 해당 메서드를 호출합니다.
  3. 현재 상태 객체는 작업을 수행하고, 필요에 따라 Context 객체의 상태를 다른 상태 객체로 변경할 수 있습니다.

상태(State) 패턴의 장단점


장점

  • 상태 관련 로직의 분리: 상태와 관련된 로직을 별도의 클래스로 분리하여 코드의 가독성과 유지보수성을 향상시킬 수 있습니다.
  • 상태 변화의 명확한 표현: 객체의 상태 변화를 명확하게 표현할 수 있으며, 상태 전이 로직을 쉽게 이해할 수 있습니다.
  • OCP (Open/Closed Principle) 준수: 새로운 상태를 추가하더라도 기존 코드를 수정할 필요가 없으므로, OCP를 준수합니다.
  • 상태 전이의 유연성: 상태 객체 내부에서 상태 전이 로직을 유연하게 구현할 수 있습니다.

단점

  • 상태가 많아질 경우 클래스의 수가 증가할 수 있습니다.

활용 예시


  • 게임 캐릭터의 상태 (걷기, 뛰기, 점프, 공격 등)
  • 네트워크 연결 상태 (연결됨, 연결 끊김, 연결 중 등)
  • 문서 편집기의 상태 (편집 모드, 읽기 모드 등)
  • 주문 처리 시스템의 상태 (주문 접수, 상품 준비, 배송 중, 배송 완료 등)

예시코드


#include <iostream>

// 상태 인터페이스
class RadioState {
public:
    virtual void On(class Radio* radio) = 0;
    virtual void Off(class Radio* radio) = 0;
    virtual void Play(class Radio* radio) = 0;
    virtual ~RadioState() {}
};

// 구체적인 상태: 켜짐 상태
class RadioOn : public RadioState {
public:
    void On(Radio* radio) override {
        std::cout << "Radio is already ON" << std::endl;
    }
    void Off(Radio* radio) override {
        std::cout << "Turning Radio OFF" << std::endl;
        radio->SetState(new RadioOff());
        delete this;
    }
    void Play(Radio* radio) override{
        std::cout << "Playing Radio" << std::endl;
    }
};

// 구체적인 상태: 꺼짐 상태
class RadioOff : public RadioState {
public:
    void On(Radio* radio) override {
        std::cout << "Turning Radio ON" << std::endl;
        radio->SetState(new RadioOn());
        delete this;
    }
    void Off(Radio* radio) override {
        std::cout << "Radio is already OFF" << std::endl;
    }
    void Play(Radio* radio) override{
        std::cout << "Can't Play. Radio is OFF" << std::endl;
    }
};

// 컨텍스트
class Radio {
private:
    RadioState* state_;
public:
    Radio() : state_(new RadioOff()) {}
    ~Radio(){delete state_;}

    void SetState(RadioState* state) {
        state_ = state;
    }

    void On() {
        state_->On(this);
    }

    void Off() {
        state_->Off(this);
    }
        void Play(){
        state_->Play(this);
    }
};

int main() {
    Radio radio;

    radio.On(); // 라디오 켜기
    radio.Play(); // 라디오 재생
    radio.Off(); // 라디오 끄기
    radio.Play(); // 라디오 재생 시도 (꺼진 상태)
    radio.Off(); // 라디오 끄기 (이미 꺼진 상태)
    radio.On(); // 라디오 켜기
    radio.On(); // 이미 켜진 상태

    return 0;
}

Top