상태 패턴은, 행동과 상태를 나눈 패턴이다.
행동을 인터페이스로 정의하여, 상태에 따라 행동들을 분류 시킨다.
FSM (Finite-State Machine), 즉 유한 상태 기계에 맞추어 패턴화 시킨 것이다.
FSM은 '유한 상태 오토마타'로도 불리는데, 이는 상태와 행동들을 노드로 연결시켜 도식화한 것을 말한다.
간단한 예로, 모험가가 있다.
모험가는 휴식, 이동, 먹기, 공격의 4가지 행동을 가지고 있다.
모험가는 배고픔, 싸움, 휴식, 이동, 죽음의 5가지 상태를 가진다.
모험가는 배고픔 상태에서 휴식 행동을 하면 음식을 찾는다.
모험가는 배고픔 상태에서 이동 행동을 하면 움직일 수 없다.
모험가는 배고픔 상태에서 먹기 행동을 하면 식사를 한다. (→휴식 상태)
모험가는 배고픈 상태에서 공격 행동을 하면 싸움에서 져서 죽는다. (→죽음 상태)
모험가는 싸움 상태에서 휴식 행동을 할 수 없다.
모험가는 싸움 상태에서 이동 행동을 하면 도망 갈 수 없다.
모험가는 싸움 상태에서 먹기 행동을 하면 식사를 할 수 없다.
모험가는 싸움 상태에서 공격 행동을 하면 싸움에서 이긴다. (→배고픔 상태)
모험가는 휴식 상태에서 휴식 행동을 하면 계속해서 휴식을 한다.
모험가는 휴식 상태에서 이동 행동을 하면 걷는다. (→이동 상태)
모험가는 휴식 상태에서 먹기 행동를 하면 쉬면서 식사를 한다.
모험가는 휴식 상태에서 공격 행동을 하면 싸움을 시작한다. (→싸움 상태)
모험가는 이동 상태에서 휴식 행동을 하면 휴식을 한다. (→휴식 상태)
모험가는 이동 상태에서 이동 행동을 하면 계속해서 이동한다.
모험가는 이동 상태에서 먹기 행동을 하면 식사를 하며 이동한다.
모험가는 이동 상태에서 공격 행동을 하면 싸움을 시작한다. (→싸움 상태)
모험가는 죽음 상태에서 휴식 행동을 할 수 없다.
모험가는 죽음 상태에서 이동 행동을 할 수 없다.
모험가는 죽음 상태에서 먹기 행동을 할 수 없다.
모험가는 죽음 상태에서 공격 행동을 할 수 없다.
글로 보면 제대로 알아 볼 수 없다.
하지만 FSM을 사용하며 그림으로 표현한다면 쉽게 이해할 수 있을 것이다.
위와 같이 노드로 표현하는 것이 FSM 이다.
이렇게 행동과 상태로 나눈 FSM을 패턴화 시키면 된다.
앞서 말하였듯, 행동을 인터페이스화 시켜야한다.
#pragma once | |
#include "Hunter.h" | |
class HunterState | |
{ | |
public: | |
virtual void Move(Hunter* hunter) = 0; | |
virtual void Eat(Hunter* hunter) = 0; | |
virtual void Attack(Hunter* hunter) = 0; | |
virtual void Idle(Hunter* hunter) = 0; | |
protected: | |
void SetHunterState(Hunter* hunter, HunterState* state) | |
{ | |
hunter->SetState(state); | |
} | |
}; | |
class MoveState : public HunterState | |
{ | |
static MoveState* instance; | |
public: | |
static MoveState * GetInstance(); | |
virtual void Move(Hunter* hunter); | |
virtual void Eat(Hunter* hunter); | |
virtual void Attack(Hunter* hunter); | |
virtual void Idle(Hunter* hunter); | |
}; | |
class IdleState : public HunterState | |
{ | |
static IdleState* instance; | |
public: | |
static IdleState* GetInstance(); | |
virtual void Move(Hunter* hunter); | |
virtual void Eat(Hunter* hunter); | |
virtual void Attack(Hunter* hunter); | |
virtual void Idle(Hunter* hunter); | |
}; | |
class BattleState : public HunterState | |
{ | |
static BattleState* instance; | |
public: | |
static BattleState * GetInstance(); | |
virtual void Move(Hunter* hunter); | |
virtual void Eat(Hunter* hunter); | |
virtual void Attack(Hunter* hunter); | |
virtual void Idle(Hunter* hunter); | |
}; | |
class HungryState : public HunterState | |
{ | |
static HungryState* instance; | |
public: | |
static HungryState * GetInstance(); | |
virtual void Move(Hunter* hunter); | |
virtual void Eat(Hunter* hunter); | |
virtual void Attack(Hunter* hunter); | |
virtual void Idle(Hunter* hunter); | |
}; | |
class DeadState : public HunterState | |
{ | |
static DeadState* instance; | |
public: | |
static DeadState* GetInstance(); | |
virtual void Move(Hunter* hunter); | |
virtual void Eat(Hunter* hunter); | |
virtual void Attack(Hunter* hunter); | |
virtual void Idle(Hunter* hunter); | |
}; |
#include "HunterState.h" | |
BattleState* BattleState::instance; | |
MoveState* MoveState::instance; | |
HungryState* HungryState::instance; | |
IdleState* IdleState::instance; | |
DeadState* DeadState::instance; | |
BattleState * BattleState::GetInstance() | |
{ | |
if (instance == nullptr) | |
{ | |
instance = new BattleState(); | |
} | |
return instance; | |
} | |
void BattleState::Move(Hunter * hunter) | |
{ | |
cout << "피할 수가 없다!" << endl; | |
} | |
void BattleState::Eat(Hunter * hunter) | |
{ | |
cout << "먹을 수가 없다!" << endl; | |
} | |
void BattleState::Attack(Hunter * hunter) | |
{ | |
cout << "전투에서 이겼다!!" << endl; | |
SetHunterState(hunter, HungryState::GetInstance()); | |
} | |
void BattleState::Idle(Hunter * hunter) | |
{ | |
cout << "휴식을 취할 수 없다!" << endl; | |
} | |
MoveState * MoveState::GetInstance() | |
{ | |
if (instance == nullptr) | |
{ | |
instance = new MoveState(); | |
} | |
return instance; | |
} | |
void MoveState::Move(Hunter * hunter) | |
{ | |
cout << "걷는중..." << endl; | |
} | |
void MoveState::Eat(Hunter * hunter) | |
{ | |
cout << "식사중..." << endl; | |
} | |
void MoveState::Attack(Hunter * hunter) | |
{ | |
cout << "야생 동물 발견! 야생 동물을 공격 한다!!" << endl; | |
SetHunterState(hunter, BattleState::GetInstance()); | |
} | |
void MoveState::Idle(Hunter * hunter) | |
{ | |
cout << "쉬는중!" << endl; | |
SetHunterState(hunter, IdleState::GetInstance()); | |
} | |
IdleState * IdleState::GetInstance() | |
{ | |
if (instance == nullptr) | |
{ | |
instance = new IdleState(); | |
} | |
return instance; | |
} | |
void IdleState::Move(Hunter * hunter) | |
{ | |
cout << "휴식 끝, 걷는중..." << endl; | |
SetHunterState(hunter, MoveState::GetInstance()); | |
} | |
void IdleState::Eat(Hunter * hunter) | |
{ | |
cout << "식사중..." << endl; | |
} | |
void IdleState::Attack(Hunter * hunter) | |
{ | |
cout << "야생 동물 발견! 야생 동물을 공격 한다!!" << endl; | |
SetHunterState(hunter, BattleState::GetInstance()); | |
} | |
void IdleState::Idle(Hunter * hunter) | |
{ | |
cout << "더 쉬고 싶다..." << endl; | |
} | |
HungryState * HungryState::GetInstance() | |
{ | |
if (instance == nullptr) | |
{ | |
instance = new HungryState(); | |
} | |
return instance; | |
} | |
void HungryState::Move(Hunter * hunter) | |
{ | |
cout << "배가 고프다..." << endl; | |
} | |
void HungryState::Eat(Hunter * hunter) | |
{ | |
cout << "식사중..." << endl; | |
SetHunterState(hunter, IdleState::GetInstance()); | |
} | |
void HungryState::Attack(Hunter * hunter) | |
{ | |
cout << "배가 고파 야생 동물에게 죽었다." << endl; | |
SetHunterState(hunter, DeadState::GetInstance()); | |
} | |
void HungryState::Idle(Hunter * hunter) | |
{ | |
cout << "먹을 걸 찾는중..." << endl; | |
} | |
DeadState * DeadState::GetInstance() | |
{ | |
if (instance == nullptr) | |
{ | |
instance = new DeadState(); | |
} | |
return instance; | |
} | |
void DeadState::Move(Hunter * hunter) | |
{ | |
cout << "사망..." << endl; | |
} | |
void DeadState::Eat(Hunter * hunter) | |
{ | |
cout << "사망..." << endl; | |
} | |
void DeadState::Attack(Hunter * hunter) | |
{ | |
cout << "시체가 공격 받고 있다." << endl; | |
} | |
void DeadState::Idle(Hunter * hunter) | |
{ | |
cout << "사망..." << endl; | |
} |
행동을 인터페이스화 시키고 나면, 행동 클래스를 상속받아서 각 각의 상태 클래스로 구현해주어야 한다.
상태에 따른 행동들을 구현하는 것만 알아두면 된다.
#pragma once | |
#include "Base.h" | |
class HunterState; | |
class Hunter | |
{ | |
HunterState* state; | |
public: | |
Hunter(); | |
void SetState(HunterState* state); | |
void Move(); | |
void Eat(); | |
void Attack(); | |
void Idle(); | |
}; |
#include "Hunter.h" | |
#include "HunterState.h" | |
Hunter::Hunter() | |
{ | |
state = IdleState::GetInstance(); | |
} | |
void Hunter::SetState(HunterState * state) | |
{ | |
this->state = state; | |
} | |
void Hunter::Move() | |
{ | |
state->Move(this); | |
} | |
void Hunter::Eat() | |
{ | |
state->Eat(this); | |
} | |
void Hunter::Attack() | |
{ | |
state->Attack(this); | |
} | |
void Hunter::Idle() | |
{ | |
state->Idle(this); | |
} |
상태 패턴으로 구현시킨 클래스를 사용할 개체를 선언한 뒤, 그 개체에 행동들을 구현하여 사용하면 된다.
그러면 미리 정의해둔 FSM에 따라서 상태에 따른 행동을 한다.
#include "Hunter.h" | |
int main() | |
{ | |
/* State */ | |
Hunter* hunter = new Hunter(); | |
hunter->Move(); | |
hunter->Move(); | |
hunter->Attack(); | |
hunter->Move(); | |
hunter->Attack(); | |
hunter->Eat(); | |
hunter->Move(); | |
hunter->Attack(); | |
hunter->Attack(); | |
hunter->Attack(); | |
hunter->Idle(); | |
delete hunter; | |
} |
휴식 끝, 걷는중... | |
걷는중... | |
야생 동물 발견! 야생 동물을 공격 한다!! | |
피할 수가 없다!! | |
전투에서 이겼다!! | |
식사중... | |
휴식 끝, 걷는중... | |
야생 동물 발견! 야생 동물을 공격한다!! | |
전투에서 이겼다!! | |
배가 고파 야생 동물에게 죽었다. | |
사망... |
'프로그래밍 끄적 > 디자인 패턴 (Design Pattern)' 카테고리의 다른 글
[디자인 패턴] 템플릿 메서드 패턴 (Template Method Pattern) (0) | 2018.09.08 |
---|---|
[디자인 패턴] 전략 패턴 (Strategy Pattern) (0) | 2018.09.06 |
[디자인 패턴] 감시자 패턴 (Observer Pattern) (0) | 2018.09.03 |
[디자인 패턴] 메멘토 패턴 (Memento Pattern) (0) | 2018.08.25 |
[디자인 패턴] 중재자 패턴 (Mediator Pattern) (2) | 2018.08.22 |