객체의 내부 상태가 바뀜에 따라 객체의 행동을 변경할 수 있게 해주는 행동 디자인 패턴
- WeaponState : State - 인터페이스
- Magic,Melee,FirearmsWeapon : Concrete States - 인터페이스를 구현한 구체적인 상태
- Weapon : Context - 여러 무기 상태를 가지는 객체
#include <iostream> #include <string> class Weapon; // 전방 선언 // 상태를 표현하는 추상 기본 클래스 (State) class WeaponState { public: virtual ~WeaponState() = default; virtual void Attack(Weapon* weapon) = 0; virtual std::string getName() = 0; }; // 상태를 가지는 주체 객체 (Context) class Weapon { private: WeaponState* state; // 현재 상태를 가리키는 포인터 public: Weapon() : state(nullptr) {} ~Weapon() { delete state; } void setState(WeaponState* newState) { delete state; // 이전 상태는 삭제 state = newState; if (state) { std::cout << "[ " << state->getName() << " 장착 ]" << std::endl; } } void Attack() { if (state) { // 실제 행동은 현재 상태 객체에게 위임 state->Attack(this); } else { std::cout << "무기가 없습니다" << std::endl; } } std::string getCurrentWeapon() { return state ? state->getName() : "없음"; } }; // 구체적인 상태 클래스들 (Concrete States) class FirearmsWeapon : public WeaponState { private: int ammo = 5; public: void Attack(Weapon* weapon) override; std::string getName() override { return "총기"; } }; class MagicWeapon : public WeaponState { private: int mana = 3; public: void Attack(Weapon* weapon) override; std::string getName() override { return "마법"; } }; class MeleeWeapon : public WeaponState { public: void Attack(Weapon* weapon) override { std::cout << "근접 공격!" << std::endl; } std::string getName() override { return "근접무기"; } }; // 각 상태 클래스의 행동 정의 void FirearmsWeapon::Attack(Weapon* weapon) { if (ammo > 0) { std::cout << "총기 공격! (남은 탄약: " << --ammo << ")" << std::endl; } else { std::cout << "탄약 소진! 근접 무기로 자동 전환" << std::endl; // 상태 스스로가 Context의 상태를 전환시킴 weapon->setState(new MeleeWeapon()); } } void MagicWeapon::Attack(Weapon* weapon) { if (mana > 0) { std::cout << "마법 공격! (남은 마나: " << --mana << ")" << std::endl; } else { std::cout << "마나 소진! 근접 무기로 자동 전환" << std::endl; // 상태 스스로가 Context의 상태를 전환시킴 weapon->setState(new MeleeWeapon()); } } int main() { Weapon weapon; std::cout << "=== 총기 테스트 ===" << std::endl; weapon.setState(new FirearmsWeapon()); weapon.Attack(); weapon.Attack(); weapon.Attack(); weapon.Attack(); weapon.Attack(); // 탄약 소진 -> 자동 전환 weapon.Attack(); // 근접 공격 std::cout << "\n=== 마법 테스트 ===" << std::endl; weapon.setState(new MagicWeapon()); weapon.Attack(); weapon.Attack(); weapon.Attack(); // 마나 소진 -> 자동 전환 weapon.Attack(); std::cout << "\n현재 무기: " << weapon.getCurrentWeapon() << std::endl; return 0; }
- 행동의 위임 : Weapon 클래스의 Attack 메서드는 직접 공격 로직을 가지고 있지 않다. 대신, 현재 state 포인터가 가리키는 객체(FirearmsWeapon이나 MagicWeapon 등)에게 행동을 위임한다.
- 상태와 행동의 캡슐화: 각 무기(상태)에 따른 행동 로직과 데이터(탄약, 마나)가 해당 상태 클래스 안에 완벽하게 캡슐화되어 있습니다. Weapon 클래스는 이 세부사항을 알 필요가 없다.
- 상태 전환: 이 패턴의 가장 강력한 특징 중 하나는 상태 객체 스스로가 다음 상태를 결정하고 전환할 수 있다는 점이다. 코드에서 보듯이 총알이나 마나가 떨어지면, FirearmsWeapon과 MagicWeapon 객체가 스스로 weapon->setState(new MeleeWeapon())를 호출하여 Weapon의 상태를 바꾼다.