Menu
Coddy logo textTech

상태 패턴

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

상태 패턴(State pattern)은 객체의 내부 상태가 변경될 때 객체가 동작을 변경할 수 있도록 허용하여, 마치 객체의 클래스가 바뀐 것처럼 보이게 합니다. 여러 상태를 처리하기 위해 복잡한 조건문을 사용하는 대신, 각 상태를 별도의 클래스로 캡슐화합니다.

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

#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() 동작을 다르게 처리하며 다음 상태로의 전이를 책임집니다. Document는 전이 로직을 알 필요가 없습니다. 단순히 현재 상태에 위임할 뿐입니다. 이는 거대한 switch 문을 제거하고 새로운 상태를 추가하는 것을 간단하게 만듭니다.

객체의 행동이 상태에 크게 의존하고, 객체의 상태에 따라 전환되는 조건문이 많을 때 State 패턴을 사용하세요.

challenge icon

챌린지

쉬움

상태(State) 패턴을 사용하여 신호등 컨트롤러(Traffic Light Controller)를 만들어 보겠습니다. 신호등이 서로 다른 상태(Red, Yellow, Green)를 순환하며, 각 상태가 신호가 바뀔 때 일어나는 일과 표시되는 메시지를 결정하는 시스템을 만들 것입니다. 이것은 상태 패턴의 전형적인 예시로, 신호등의 동작은 전적으로 현재 상태에 따라 달라집니다.

코드는 다음 세 개의 파일로 구성됩니다:

  • TrafficLightState.h: 상태 인터페이스와 모든 구체적인 상태 클래스들을 정의합니다.

    다음 내용을 포함하는 추상 TrafficLightState 클래스를 생성하세요:

    • 다음 상태로의 전환을 처리하는 순수 가상 메서드 change(TrafficLight& light)
    • 현재 색상을 문자열로 반환하는 순수 가상 메서드 getColor()
    • 운전자가 해야 할 행동(예: "Stop", "Caution", "Go")을 반환하는 순수 가상 메서드 getAction()
    • 가상 소멸자

    세 가지 구체적인 상태를 구현하세요:

    • RedState — 색상은 "Red", 행동은 "Stop"이며, Green으로 변경됩니다.
    • YellowState — 색상은 "Yellow", 행동은 "Caution"이며, Red로 변경됩니다.
    • GreenState — 색상은 "Green", 행동은 "Go"이며, Yellow로 변경됩니다.

    상태 클래스들이 TrafficLight를 참조하므로 TrafficLight에 대한 전방 선언(forward declaration)이 필요합니다.

  • TrafficLight.h: 현재 상태를 유지하는 컨텍스트(context) 클래스를 생성합니다.

    TrafficLight 클래스는 std::unique_ptr<TrafficLightState>를 보유해야 하며 기본적으로 Red 상태에서 시작해야 합니다. 다음을 구현하세요:

    • setState(std::unique_ptr<TrafficLightState> newState) — 현재 상태를 변경합니다.
    • change() — 현재 상태의 change 메서드에 위임합니다.
    • display() — 신호등의 상태를 [Color]: [Action] 형식으로 출력합니다.
  • main.cpp: 신호등이 상태를 순환하는 것을 보여줍니다.

    하나의 입력을 읽습니다: 수행할 상태 변경 횟수(정수).

    TrafficLight를 생성하고 초기 상태를 표시합니다. 그런 다음 지정된 횟수만큼 변경을 수행하며, 각 변경 후의 상태를 표시합니다.

예를 들어, 입력이 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 클래스는 어떤 일이 일어날지 결정하기 위한 조건부 로직이 필요하지 않으며, 단순히 현재 상태에 모든 것을 위임합니다.

직접 해보기

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

using namespace std;

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

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

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