C++ State 패턴
객체의 내부 상태에 따라 행동이 달라지는 상태 머신 패턴입니다. 미디어 플레이어의 재생/정지/일시정지 상태 전환을 예시로 구현합니다.
#include <iostream>
#include <memory>
#include <string>
#include <stdexcept>
// ── 전방 선언 ─────────────────────────────────────────────────────────────────
class MediaPlayer;
// ── 상태 인터페이스 ───────────────────────────────────────────────────────────
class PlayerState {
public:
virtual ~PlayerState() = default;
virtual void play(MediaPlayer& player) = 0;
virtual void pause(MediaPlayer& player) = 0;
virtual void stop(MediaPlayer& player) = 0;
virtual void next(MediaPlayer& player) = 0;
virtual std::string name() const = 0;
};
// ── 컨텍스트: 미디어 플레이어 ────────────────────────────────────────────────
class MediaPlayer {
public:
explicit MediaPlayer(std::string trackName)
: trackName_(std::move(trackName)), position_(0) {}
// 상태 전환 — 상태 객체가 직접 호출
void setState(std::shared_ptr<PlayerState> newState) {
std::cout << " [전환] " << (state_ ? state_->name() : "없음")
<< " → " << newState->name() << '\n';
state_ = std::move(newState);
}
// 사용자 액션 — 현재 상태에 위임
void play() { state_->play(*this); }
void pause() { state_->pause(*this); }
void stop() { state_->stop(*this); }
void next() { state_->next(*this); }
// 트랙 정보 접근자
const std::string& trackName() const { return trackName_; }
void setTrack(std::string name) { trackName_ = std::move(name); position_ = 0; }
int position() const { return position_; }
void advancePosition(int ms) { position_ += ms; }
void resetPosition() { position_ = 0; }
void printStatus() const {
std::cout << " [상태] " << (state_ ? state_->name() : "없음")
<< " | 트랙: " << trackName_
<< " | 위치: " << position_ << "ms\n";
}
private:
std::shared_ptr<PlayerState> state_;
std::string trackName_;
int position_;
};
// ── 구체 상태: 정지 ───────────────────────────────────────────────────────────
class StoppedState final : public PlayerState {
public:
static std::shared_ptr<StoppedState> instance() {
static auto inst = std::make_shared<StoppedState>();
return inst;
}
void play(MediaPlayer& player) override; // 전방 선언 — 아래 정의
void pause(MediaPlayer& player) override {
std::cout << " [무시] 정지 상태에서 일시정지 불가\n";
}
void stop(MediaPlayer& player) override {
std::cout << " [무시] 이미 정지 상태입니다.\n";
}
void next(MediaPlayer& player) override;
std::string name() const override { return "정지"; }
};
// ── 구체 상태: 재생 ───────────────────────────────────────────────────────────
class PlayingState final : public PlayerState {
public:
static std::shared_ptr<PlayingState> instance() {
static auto inst = std::make_shared<PlayingState>();
return inst;
}
void play(MediaPlayer& player) override {
std::cout << " [무시] 이미 재생 중입니다.\n";
}
void pause(MediaPlayer& player) override; // 아래 정의
void stop(MediaPlayer& player) override;
void next(MediaPlayer& player) override;
std::string name() const override { return "재생"; }
};
// ── 구체 상태: 일시정지 ───────────────────────────────────────────────────────
class PausedState final : public PlayerState {
public:
static std::shared_ptr<PausedState> instance() {
static auto inst = std::make_shared<PausedState>();
return inst;
}
void play(MediaPlayer& player) override;
void pause(MediaPlayer& player) override {
std::cout << " [무시] 이미 일시정지 상태입니다.\n";
}
void stop(MediaPlayer& player) override;
void next(MediaPlayer& player) override;
std::string name() const override { return "일시정지"; }
};
// ── 상태 전환 로직 구현 (모든 클래스 선언 후) ────────────────────────────────
void StoppedState::play(MediaPlayer& p) {
p.resetPosition();
p.setState(PlayingState::instance());
std::cout << " ▶ '" << p.trackName() << "' 재생 시작\n";
}
void StoppedState::next(MediaPlayer& p) {
p.setTrack("다음 트랙");
std::cout << " ⏭ 다음 트랙으로 전환: '" << p.trackName() << "'\n";
}
void PlayingState::pause(MediaPlayer& p) {
p.advancePosition(30'000); // 30초 진행 가정
p.setState(PausedState::instance());
std::cout << " ⏸ 일시정지 (위치: " << p.position() << "ms)\n";
}
void PlayingState::stop(MediaPlayer& p) {
p.resetPosition();
p.setState(StoppedState::instance());
std::cout << " ⏹ 재생 정지\n";
}
void PlayingState::next(MediaPlayer& p) {
p.setTrack("다음 트랙");
std::cout << " ⏭ 재생 중 다음 트랙: '" << p.trackName() << "'\n";
}
void PausedState::play(MediaPlayer& p) {
p.setState(PlayingState::instance());
std::cout << " ▶ '" << p.trackName() << "' 이어서 재생 (위치: " << p.position() << "ms)\n";
}
void PausedState::stop(MediaPlayer& p) {
p.resetPosition();
p.setState(StoppedState::instance());
std::cout << " ⏹ 일시정지에서 정지\n";
}
void PausedState::next(MediaPlayer& p) {
p.setTrack("다음 트랙");
p.setState(StoppedState::instance());
std::cout << " ⏭ 다음 트랙으로 전환 후 정지: '" << p.trackName() << "'\n";
}
// ── 사용 예시 ─────────────────────────────────────────────────────────────────
int main() {
MediaPlayer player("Bohemian Rhapsody");
player.setState(StoppedState::instance());
player.printStatus();
std::cout << "\n--- 재생 시작 ---\n";
player.play();
player.printStatus();
std::cout << "\n--- 일시정지 ---\n";
player.pause();
player.printStatus();
std::cout << "\n--- 이어서 재생 ---\n";
player.play();
player.printStatus();
std::cout << "\n--- 재생 중 다음 트랙 ---\n";
player.next();
player.printStatus();
std::cout << "\n--- 정지 ---\n";
player.stop();
player.printStatus();
std::cout << "\n--- 정지 상태에서 일시정지 시도 (무시) ---\n";
player.pause();
}