Menu
Coddy logo textTech

상태 패턴

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 104개 중 100번째.

상태 패턴을 사용하면 객체의 내부 상태가 변경될 때 객체의 동작을 변경할 수 있으므로, 객체의 class가 변경된 것처럼 보이게 할 수 있습니다. 서로 다른 상태를 처리하기 위해 복잡한 조건문을 사용하는 대신, 각 상태를 별도의 class로 캡슐화합니다.

이 패턴은 현재 상태에 대한 참조를 유지하는 Context, 상태별 동작을 정의하는 상태 interface, 그리고 각 상태에 대한 동작을 구현하는 Concrete 상태로 구성됩니다:

#include <iostream>
#include <memory>

class Document;  // 전방 선언

// 상태 인터페이스
class DocumentState {
public:
    virtual void publish(Document& doc) = 0;
    virtual std::string getName() const = 0;
    virtual ~DocumentState() = default;
};

// 컨텍스트
class Document {
    std::unique_ptr<DocumentState> state;
public:
    Document();
    void setState(std::unique_ptr<DocumentState> newState) {
        state = std::move(newState);
    }
    void publish() { state->publish(*this); }
    std::string getStateName() const { return state->getName(); }
};

// 구체 상태
class Draft : public DocumentState {
public:
    void publish(Document& doc) override;
    std::string getName() const override { return "Draft"; }
};

class Review : public DocumentState {
public:
    void publish(Document& doc) override;
    std::string getName() const override { return "Review"; }
};

class Published : public DocumentState {
public:
    void publish(Document& doc) override {
        std::cout << "Already published\n";
    }
    std::string getName() const override { return "Published"; }
};

void Draft::publish(Document& doc) {
    std::cout << "Moving to review\n";
    doc.setState(std::make_unique<Review>());
}

void Review::publish(Document& doc) {
    std::cout << "Publishing document\n";
    doc.setState(std::make_unique<Published>());
}

Document::Document() : state(std::make_unique<Draft>()) {}

각 상태는 publish() action을 서로 다르게 처리하며 다음 상태로 전환하는 책임을 집니다. Document는 전환 로직을 알 필요가 없습니다. 단지 current 상태에 delegates합니다. 이를 통해 대규모 switch 문이 사라지고 새로운 상태를 간단하게 추가할 수 있습니다.

객체의 동작이 해당 객체의 상태에 크게 의존하고 객체의 상태에 따라 전환되는 조건문이 많을 때 State를 사용하세요.

challenge icon

챌린지

쉬움

State 패턴을 사용하여 Traffic Light Controller를 만들어 봅시다. 교통 신호가 서로 다른 상태(Red, Yellow, Green)를 순환하고, 각 상태가 신호가 change될 때 발생하는 일과 표시되는 메시지를 결정하는 시스템을 만들게 됩니다. 이는 State 패턴의 전형적인 예입니다. 교통 신호의 동작은 전적으로 현재 상태에 따라 달라집니다.

코드를 세 개의 파일로 구성합니다.

  • TrafficLightState.h: 상태 interface와 모든 Concrete 상태 class를 define합니다.

    Abstract TrafficLightState class를 다음과 같이 만듭니다.

    • 다음 상태로 전환하는 change를 처리하는 순수 virtual method change(TrafficLight& light)
    • 현재 색상을 문자열로 반환하는 순수 virtual method getColor()
    • 운전자가 해야 할 일을 반환하는 순수 virtual method getAction() (예: "Stop", "Caution", "Go")
    • virtual destructor

    세 가지 Concrete 상태를 Implement합니다.

    • RedState: 색상은 "Red", action은 "Stop"이며 Green으로 change합니다.
    • YellowState: 색상은 "Yellow", action은 "Caution"이며 Red로 change합니다.
    • GreenState: 색상은 "Green", action은 "Go"이며 Yellow로 change합니다.

    상태가 TrafficLight를 참조하므로 TrafficLight에 대한 forward declaration이 필요합니다.

  • TrafficLight.h: current 상태를 유지하는 context class를 만듭니다.

    TrafficLight class는 std::unique_ptr<TrafficLightState>를 보유하고, default로 Red 상태에서 시작해야 합니다. 다음을 Implement합니다.

    • setState(std::unique_ptr<TrafficLightState> newState): current 상태를 변경합니다.
    • change(): current 상태의 change method에 delegates합니다.
    • display(): 신호의 상태를 [Color]: [Action] 형식으로 출력합니다.
  • main.cpp: 상태를 순환하는 교통 신호를 보여 줍니다.

    입력 하나를 읽습니다. 수행할 상태 변경 횟수(integer)입니다.

    TrafficLight를 만들고 initial 상태를 표시합니다. 그런 다음 지정된 횟수만큼 change를 수행하고, 각 change 후에 상태를 표시합니다.

예를 들어 입력이 3인 경우:

Red: Stop
Green: Go
Yellow: Caution
Red: Stop

입력이 6인 경우:

Red: Stop
Green: Go
Yellow: Caution
Red: Stop
Green: Go
Yellow: Caution
Red: Stop

교통 신호가 예측 가능한 패턴으로 상태를 순환하는 것을 확인할 수 있습니다: Red → Green → Yellow → Red. 각 상태는 다음에 올 상태를 알고 있으며 자체 전환을 처리합니다. TrafficLight class는 어떤 일이 발생할지 결정하기 위해 conditional logic을 사용할 필요가 없습니다. 단순히 현재 속한 상태에 delegates합니다.

직접 해보기

#include <iostream>
#include "TrafficLight.h"

using namespace std;

int main() {
    // 상태 변경 횟수를 읽습니다
    int numChanges;
    cin >> numChanges;
    
    // TODO: TrafficLight 객체를 생성하세요
    
    // TODO: 초기 상태를 표시하세요
    
    // TODO: 지정된 횟수만큼 변경을 수행하세요
    // 각 변경 후에 현재 상태를 표시하세요
    
    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 C++ 컴파일러