Difference between revisions of "State"
(→Description) |
(→References) |
||
(29 intermediate revisions by the same user not shown) | |||
Line 5: | Line 5: | ||
=== What is the State Pattern? === | === What is the State Pattern? === | ||
− | + | <ul> | |
+ | <li>Allow to sub-divide the behavior of an object depending on its current state.</li> | ||
+ | <li>Classified as a behavior pattern.</li> | ||
+ | <li>Also known as Objects for States.</li> | ||
+ | </ul> | ||
=== When do you use the State Pattern? === | === When do you use the State Pattern? === | ||
− | Suppose that you are playing War Craft. Your units will behave according to your | + | Suppose that you are playing War Craft. Your units will behave according to your commands. For example, if the "Attack" command is given to your units, the state of the units will be changed to "Attack State" and they will attack the enemies around them. If you set them to hold their position, their state will be changed to "Hold Position" and your units will stop attacking and stand around. These behaviors such as "attack" behavior and "hold position" behavior can be also applied to other species – there are five species in this game; undeads, orcs, night elves, humans and corrupted night elves. By the use of the state pattern, it is possible to save time and gain reusability and maintainability since you do not have to code the similar unit behaviors all over again for other species when programming such game. |
=== Advantages of using State Pattern === | === Advantages of using State Pattern === | ||
Line 19: | Line 23: | ||
'''2. State pattern makes state transition explicit. ''' | '''2. State pattern makes state transition explicit. ''' | ||
− | If the state is defined with an internal data values, its state transition do not have explicit representation. If a variable is used to specify its current state, it will | + | If the state is defined with an internal data values, its state transition do not have explicit representation. If a variable is used to specify its current state, it will be difficult to extend and maintain since the states and transitions are handled using many if and switch statements. |
'''3. State objects can be shared by other classes.''' | '''3. State objects can be shared by other classes.''' | ||
− | As | + | As mentioned above, state object can be used by other classes. For example, all five species of the War Craft have common behaviors such as "Attack" and "Hold Position" behaviors, and those state-specified behaviors can be used by all character classes. Click the link below to see the examples. |
== UML Diagram == | == UML Diagram == | ||
Line 32: | Line 36: | ||
== Code Samples == | == Code Samples == | ||
− | |||
− | [http://matrix.senecac.on.ca/~rmwang/btppresentation/sunsample.html Click here to see the UML diagram for this example] | + | === Greeting Message Generator === |
+ | |||
+ | '''[http://matrix.senecac.on.ca/~rmwang/btppresentation/sunsample.html Click here to see the UML diagram for this example]''' | ||
+ | |||
+ | '''State class interface definition''' | ||
+ | <pre> | ||
+ | class CBaseState | ||
+ | { | ||
+ | public: | ||
+ | virtual CBaseState* GetNextState() = 0; | ||
+ | virtual char* ToString() = 0; | ||
+ | }; | ||
+ | </pre> | ||
+ | '''Concrete class definitions''' | ||
+ | <pre> | ||
+ | |||
+ | class CMorning : public CBaseState{ | ||
+ | public: | ||
+ | virtual CBaseState* GetNextState(); | ||
+ | virtual char* ToString(); | ||
+ | }; | ||
+ | class CEvening : public CBaseState{ | ||
+ | public: | ||
+ | virtual CBaseState* GetNextState(); | ||
+ | virtual char* ToString(); | ||
+ | }; | ||
+ | |||
+ | class CNight: public CBaseState{ | ||
+ | public: | ||
+ | virtual CBaseState* GetNextState(); | ||
+ | virtual char* ToString(); | ||
+ | }; | ||
+ | </pre> | ||
+ | '''Context class structure''' | ||
+ | <pre> | ||
+ | class CSun{ | ||
+ | public: | ||
+ | CSun(); | ||
+ | CSun(CBaseState* pContext); | ||
+ | ~CSun(); | ||
+ | void StateChanged(); | ||
+ | char* GetStateName(); | ||
+ | protected: | ||
+ | void DoCleanUp(); | ||
+ | CBaseState* m_pState; | ||
+ | }; | ||
+ | </pre> | ||
+ | '''Function definition for state change''' | ||
+ | <pre> | ||
+ | void CSun::StateChanged(){ | ||
+ | if (m_pState){ | ||
+ | CBaseState* pState = m_pState->GetNextState(); | ||
+ | delete m_pState; | ||
+ | m_pState = pState; | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | '''Code that changes state''' | ||
+ | <pre> | ||
+ | CBaseState* CMorning::GetNextState(){return new CEvening;} | ||
+ | CBaseState* CEvening::GetNextState(){return new CNight;} | ||
+ | CBaseState* CNight::GetNextState(){return new CMorning;} | ||
+ | </pre> | ||
+ | '''Simple example of actual implementation''' | ||
+ | <pre> | ||
+ | CSun objSun(new CMorning); | ||
+ | printf("\n\nSun Says Good %s !!!",objSun.GetStateName()); | ||
+ | objSun.StateChanged(); | ||
+ | printf("\n\nSun Says Good %s !!!",objSun.GetStateName()); | ||
+ | objSun.StateChanged(); | ||
+ | printf("\n\nSun Says Good %s !!!",objSun.GetStateName()); | ||
+ | objSun.StateChanged(); | ||
+ | printf("\n\nSun Says Good %s !!!",objSun.GetStateName()); | ||
+ | </pre> | ||
+ | |||
+ | === Game programming: Character Status === | ||
+ | '''[http://matrix.senecac.on.ca/~rmwang/btppresentation/statesample.html Click here to see the UML diagram for this example]''' | ||
+ | |||
+ | <br /> | ||
+ | '''Header File Content''' | ||
+ | <pre> | ||
+ | class IState; | ||
+ | class cDarkElf : public cUnit { | ||
+ | IState* _pState; | ||
+ | void ChangeState(IState*); | ||
+ | public: | ||
+ | cDarkElf(); | ||
+ | ~cDarkElf(); | ||
+ | void Attack(cUnit* Target); | ||
+ | void Move(int x, int y); | ||
+ | void Stop(); | ||
+ | void HoldPosition(); | ||
+ | void Slaughter(cUnit* Target); | ||
+ | void Update(); | ||
+ | }; | ||
+ | </pre> | ||
+ | '''Class Definitions for State objects''' | ||
+ | <pre> | ||
+ | class IState { | ||
+ | const ID_STATE _ID; | ||
+ | public: | ||
+ | enum ID_STATE {STATE_ATTACK, STATE_MOVE, STATE_STOP, | ||
+ | STATE_HOLDPOSITION, STATE_SLAUGHTER}; | ||
+ | IState(ID_STATE state) : _ID(state) {} | ||
+ | ID_STATE GetID() {return _ID;} | ||
+ | virtual void Operate() = 0; | ||
+ | }; | ||
+ | |||
+ | class cState_Attack : public IState { | ||
+ | cUnit* _pTarget; | ||
+ | cUnit* _pUnit; | ||
+ | public: | ||
+ | cState_Attack(cUnit* target, cUnit* pUnit) : | ||
+ | IState(STATE_ATTACK), _pTarget(target), _pUnit(pUnit){} | ||
+ | void Operate(); | ||
+ | }; | ||
+ | |||
+ | class cState_Move : public IState { | ||
+ | int _x, _y; | ||
+ | cUnit* _pUnit; | ||
+ | public: | ||
+ | cState_Move(int x, int y, cUnit* pUnit); | ||
+ | void Operate(); | ||
+ | }; | ||
+ | |||
+ | class cState_Stop : public IState { | ||
+ | public: | ||
+ | void Operate(); | ||
+ | }; | ||
+ | |||
+ | class cState_HoldPosition : public IState { | ||
+ | public: | ||
+ | void Operate(); | ||
+ | }; | ||
+ | |||
+ | class cState_SLAUGHTER : public IState { | ||
+ | cUnit* _pTarget; | ||
+ | cUnit* _pUnit; | ||
+ | public: | ||
+ | cState_Slaughter(cUnit* target, cUnit* pUnit) : | ||
+ | IState(STATE_SLAUGHTER), _pTarget(target), | ||
+ | _pUnit(pUnit) {} | ||
+ | void Operate(); | ||
+ | }; | ||
+ | </pre> | ||
+ | '''Functions that changes character status''' | ||
+ | <pre> | ||
+ | cDarkElf::cDarkElf() {_pState = new cState_Stop;} | ||
+ | |||
+ | cDarkElf::~cDarkElf() {delete _pState;} | ||
+ | |||
+ | void cDarkElf::ChangeState(IState* pState) { | ||
+ | delete _pState; | ||
+ | _pState = pState; | ||
+ | } | ||
+ | |||
+ | void cDarkElf::Attack(cUnit* Target) { | ||
+ | ChangeState(new cState_Attack(Target, this)); | ||
+ | } | ||
+ | |||
+ | void cDarkElf::Move(int x, int y) { | ||
+ | ChangeState(new cState_Move(x, y, this)); | ||
+ | } | ||
+ | |||
+ | void cDarkElf::Stop() { | ||
+ | ChangeState(new cState_Stop); | ||
+ | } | ||
+ | |||
+ | void cDarkElf::HoldPosition() { | ||
+ | ChangeState(new cState_HoldPosition); | ||
+ | } | ||
+ | |||
+ | void cDarkElf::Patrol(int x1, int y1, int x2, int y2) { | ||
+ | ChangeState(new cState_Patrol(x1, y1, x2, y2)); | ||
+ | } | ||
+ | |||
+ | void cDarkElf::Update() { | ||
+ | _pState->Operate(); | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | === State Pattern Example: C# === | ||
+ | '''Abstract Class''' | ||
+ | <pre> | ||
+ | using System; | ||
+ | using System.Drawing; | ||
+ | |||
+ | namespace StatePatternApp | ||
+ | { | ||
+ | public abstract class State | ||
+ | { | ||
+ | public State() | ||
+ | { | ||
+ | } | ||
+ | public State(State state) | ||
+ | { | ||
+ | this.CurrentLevel = state.CurrentLevel; | ||
+ | this.Deviation = state.Deviation; | ||
+ | this.MaxLevel = state.MaxLevel; | ||
+ | this.MinLevel = state.MinLevel; | ||
+ | this.Machine = state.Machine; | ||
+ | this.SetParams(); | ||
+ | } | ||
+ | public State(Machine machine, int iCurrentLevel, | ||
+ | int iDeviation, int iMaxLevel, int iMinLevel) | ||
+ | { | ||
+ | this.CurrentLevel = iCurrentLevel; | ||
+ | this.Deviation = iDeviation; | ||
+ | this.MaxLevel = iMaxLevel; | ||
+ | this.MinLevel = iMinLevel; | ||
+ | this.Machine = machine; | ||
+ | this.SetParams(); | ||
+ | } | ||
+ | private Machine machine; | ||
+ | private int minLevel = 0; | ||
+ | private int maxLevel = 0; | ||
+ | private int currentLevel = 0; | ||
+ | private int deviation = 0; | ||
+ | private Color messageColor = Color.Green; | ||
+ | private string messageText = ""; | ||
+ | public virtual void SetParams(){} | ||
+ | |||
+ | public virtual void CheckEfficiency() | ||
+ | { | ||
+ | Transition t = Transition.GetInstance(); | ||
+ | t.Transform(this); | ||
+ | } | ||
+ | |||
+ | protected virtual void ChangeState(Machine m, State s) | ||
+ | { | ||
+ | m.ChangeState(s); | ||
+ | } | ||
+ | |||
+ | public Machine Machine | ||
+ | { | ||
+ | get | ||
+ | { | ||
+ | return this.machine; | ||
+ | } | ||
+ | set | ||
+ | { | ||
+ | this.machine = value; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public int MinLevel | ||
+ | { | ||
+ | get | ||
+ | { | ||
+ | return this.minLevel; | ||
+ | } | ||
+ | set | ||
+ | { | ||
+ | this.minLevel = value; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public int MaxLevel | ||
+ | { | ||
+ | get | ||
+ | { | ||
+ | return this.maxLevel; | ||
+ | } | ||
+ | set | ||
+ | { | ||
+ | this.maxLevel = value; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public int CurrentLevel | ||
+ | { | ||
+ | get | ||
+ | { | ||
+ | return this.currentLevel; | ||
+ | } | ||
+ | set | ||
+ | { | ||
+ | this.currentLevel = value; | ||
+ | // Is the machine value set? | ||
+ | if(this.Machine != null) | ||
+ | { | ||
+ | this.CheckEfficiency(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public int Deviation | ||
+ | { | ||
+ | get | ||
+ | { | ||
+ | return this.deviation; | ||
+ | } | ||
+ | set | ||
+ | { | ||
+ | this.deviation = value; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public int MaxDeviaton | ||
+ | { | ||
+ | get | ||
+ | { | ||
+ | return this.MaxLevel - this.Deviation; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public int MinDeviaton | ||
+ | { | ||
+ | get | ||
+ | { | ||
+ | return this.MinLevel + this.Deviation; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public Color MessageColor | ||
+ | { | ||
+ | get | ||
+ | { | ||
+ | return this.messageColor; | ||
+ | } | ||
+ | set | ||
+ | { | ||
+ | this.messageColor = value; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public string MessageText | ||
+ | { | ||
+ | get | ||
+ | { | ||
+ | return this.messageText; | ||
+ | } | ||
+ | set | ||
+ | { | ||
+ | this.messageText = value; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | '''Transition class''' | ||
+ | <PRE> | ||
+ | using System; | ||
+ | |||
+ | namespace StatePatternApp | ||
+ | { | ||
+ | class Transition | ||
+ | { | ||
+ | private static Transition getInstance; | ||
+ | protected Transition() {} | ||
+ | |||
+ | |||
+ | public static Transition GetInstance() | ||
+ | { | ||
+ | if(getInstance == null) | ||
+ | { | ||
+ | getInstance = new Transition(); | ||
+ | } | ||
+ | return getInstance; | ||
+ | } | ||
+ | |||
+ | public void Transform(State state) | ||
+ | { | ||
+ | if(state == null) | ||
+ | { | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | // Get the type of state. | ||
+ | string stateType = state.GetType().Name; | ||
+ | |||
+ | // Are we in normal state? | ||
+ | if(state.CurrentLevel < state.MaxDeviaton && | ||
+ | state.CurrentLevel > state.MinDeviaton) | ||
+ | { | ||
+ | if(stateType.ToUpper() != "NORMALSTATE") | ||
+ | { | ||
+ | state.ChangeState(state.Machine, | ||
+ | new NormalState(state)); | ||
+ | } | ||
+ | } | ||
+ | // Are we in warning? | ||
+ | if(state.Deviation > 0) | ||
+ | { | ||
+ | if((state.CurrentLevel < state.MaxLevel && | ||
+ | state.CurrentLevel >= state.MaxDeviaton) || | ||
+ | state.CurrentLevel > state.MinLevel && | ||
+ | state.CurrentLevel <= state.MinDeviaton) | ||
+ | { | ||
+ | if(stateType.ToUpper() != "WARNINGSTATE") | ||
+ | { | ||
+ | state.ChangeState(state.Machine, | ||
+ | new WarningState(state)); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | // Are we in alert state? | ||
+ | if(state.CurrentLevel >= state.MaxLevel || | ||
+ | state.CurrentLevel <= state.MinLevel) | ||
+ | { | ||
+ | if(stateType.ToUpper() != "ALERTSTATE") | ||
+ | { | ||
+ | state.ChangeState(state.Machine, | ||
+ | new AlertState(state)); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </PRE> | ||
+ | '''NormalState - derived state class''' | ||
+ | <PRE>using System; | ||
+ | using System.Drawing; | ||
+ | |||
+ | namespace StatePatternApp | ||
+ | { | ||
+ | public class NormalState : State | ||
+ | { | ||
+ | public NormalState(State state) : base(state) | ||
+ | { | ||
+ | } | ||
+ | |||
+ | public NormalState(Machine machine, int iCurrentLevel, | ||
+ | int iDeviation, int iMaxLevel, int iMinLevel) : | ||
+ | base(machine, iCurrentLevel, iDeviation, | ||
+ | iMaxLevel, iMinLevel) | ||
+ | { | ||
+ | } | ||
+ | |||
+ | public override void SetParams() | ||
+ | { | ||
+ | this.MessageColor = Color.Green; | ||
+ | this.MessageText = "Normal"; | ||
+ | } | ||
+ | } | ||
+ | }</PRE> | ||
+ | '''WarningState - derived state class''' | ||
+ | <PRE>using System; | ||
+ | using System.Drawing; | ||
+ | |||
+ | namespace StatePatternApp | ||
+ | { | ||
+ | public class WarningState : State | ||
+ | { | ||
+ | public WarningState(State state) : base(state) | ||
+ | { | ||
+ | } | ||
+ | |||
+ | public WarningState(Machine machine, int iCurrentLevel, | ||
+ | int iDeviation, int iMaxLevel, int iMinLevel) : | ||
+ | base(machine, iCurrentLevel, iDeviation, iMaxLevel, | ||
+ | iMinLevel) | ||
+ | { | ||
+ | } | ||
+ | |||
+ | public override void SetParams() | ||
+ | { | ||
+ | this.MessageColor = Color.Yellow; | ||
+ | this.MessageText = "Warning"; | ||
+ | } | ||
+ | } | ||
+ | }</PRE> | ||
+ | '''AlertState - derived state class''' | ||
+ | <PRE>using System; | ||
+ | using System.Drawing; | ||
+ | |||
+ | namespace StatePatternApp | ||
+ | { | ||
+ | public class AlertState : State | ||
+ | { | ||
+ | public AlertState(State state) : base(state) | ||
+ | { | ||
+ | } | ||
+ | |||
+ | public AlertState(Machine machine, int iCurrentLevel, | ||
+ | int iDeviation, int iMaxLevel, int iMinLevel) : | ||
+ | base(machine, iCurrentLevel, iDeviation, iMaxLevel, | ||
+ | iMinLevel) | ||
+ | { | ||
+ | } | ||
+ | |||
+ | public override void SetParams() | ||
+ | { | ||
+ | this.MessageColor = Color.Red; | ||
+ | this.MessageText = "Alert"; | ||
+ | } | ||
+ | } | ||
+ | }</PRE> | ||
+ | '''Machine class - This class maintains an instance of state''' | ||
+ | <PRE lang=cs id=pre5 style="MARGIN-TOP: 0px">using System; | ||
+ | |||
+ | namespace StatePatternApp | ||
+ | { | ||
+ | public class Machine | ||
+ | { | ||
+ | public State currentState; | ||
+ | |||
+ | public Machine(int iCurrentLevel, int iDeviation, | ||
+ | int iMaxLevel, int iMinLevel) | ||
+ | { | ||
+ | currentState = new NormalState(this, iCurrentLevel, | ||
+ | iDeviation, iMaxLevel, iMinLevel); | ||
+ | currentState.CheckEfficiency(); | ||
+ | } | ||
+ | |||
+ | public void ChangeState(State setState) | ||
+ | { | ||
+ | currentState = setState; | ||
+ | } | ||
+ | |||
+ | public void SetCurrentLevel(int level) | ||
+ | { | ||
+ | currentState.CurrentLevel = level; | ||
+ | } | ||
+ | } | ||
+ | }</PRE> | ||
== References == | == References == | ||
− | + | <ul> | |
− | + | <li>Erich, Gamma. Design Patterns : elements of reusable object-oriented software. Upper Saddle River: Pearson Education Corporate Sales Division, 2000.</li> | |
− | + | <li>"4th Lecture about Design Patterns". Ssamdark's Homepage. March 20, 2007 <http://www.misofruit.co.kr/seojewoo/programming/designpattern4.htm>. | |
+ | </li> | ||
+ | <li>Young, Kenny. "C# - State Pattern Example". The Code Project. April 2nd, 2007 <http://www.codeproject.com/cs/design/statepatterncsharp.asp>. | ||
+ | </li> | ||
+ | </ul> |
Latest revision as of 22:39, 12 April 2007
Contents
Description
What is the State Pattern?
- Allow to sub-divide the behavior of an object depending on its current state.
- Classified as a behavior pattern.
- Also known as Objects for States.
When do you use the State Pattern?
Suppose that you are playing War Craft. Your units will behave according to your commands. For example, if the "Attack" command is given to your units, the state of the units will be changed to "Attack State" and they will attack the enemies around them. If you set them to hold their position, their state will be changed to "Hold Position" and your units will stop attacking and stand around. These behaviors such as "attack" behavior and "hold position" behavior can be also applied to other species – there are five species in this game; undeads, orcs, night elves, humans and corrupted night elves. By the use of the state pattern, it is possible to save time and gain reusability and maintainability since you do not have to code the similar unit behaviors all over again for other species when programming such game.
Advantages of using State Pattern
1. State pattern allows object to perform state-specific behaviors and operations and partitions behavior based on its state.
State pattern is beneficial since the object's state-specific behaviors are maintained in a single object. It is much easier to create and add new states and transitions than extending behaviors using if or switch statements. New state implements the state interface, and new state can extend other states.
2. State pattern makes state transition explicit.
If the state is defined with an internal data values, its state transition do not have explicit representation. If a variable is used to specify its current state, it will be difficult to extend and maintain since the states and transitions are handled using many if and switch statements.
3. State objects can be shared by other classes.
As mentioned above, state object can be used by other classes. For example, all five species of the War Craft have common behaviors such as "Attack" and "Hold Position" behaviors, and those state-specified behaviors can be used by all character classes. Click the link below to see the examples.
UML Diagram
State Pattern UML Diagram
Code Samples
Greeting Message Generator
Click here to see the UML diagram for this example
State class interface definition
class CBaseState { public: virtual CBaseState* GetNextState() = 0; virtual char* ToString() = 0; };
Concrete class definitions
class CMorning : public CBaseState{ public: virtual CBaseState* GetNextState(); virtual char* ToString(); }; class CEvening : public CBaseState{ public: virtual CBaseState* GetNextState(); virtual char* ToString(); }; class CNight: public CBaseState{ public: virtual CBaseState* GetNextState(); virtual char* ToString(); };
Context class structure
class CSun{ public: CSun(); CSun(CBaseState* pContext); ~CSun(); void StateChanged(); char* GetStateName(); protected: void DoCleanUp(); CBaseState* m_pState; };
Function definition for state change
void CSun::StateChanged(){ if (m_pState){ CBaseState* pState = m_pState->GetNextState(); delete m_pState; m_pState = pState; } }
Code that changes state
CBaseState* CMorning::GetNextState(){return new CEvening;} CBaseState* CEvening::GetNextState(){return new CNight;} CBaseState* CNight::GetNextState(){return new CMorning;}
Simple example of actual implementation
CSun objSun(new CMorning); printf("\n\nSun Says Good %s !!!",objSun.GetStateName()); objSun.StateChanged(); printf("\n\nSun Says Good %s !!!",objSun.GetStateName()); objSun.StateChanged(); printf("\n\nSun Says Good %s !!!",objSun.GetStateName()); objSun.StateChanged(); printf("\n\nSun Says Good %s !!!",objSun.GetStateName());
Game programming: Character Status
Click here to see the UML diagram for this example
Header File Content
class IState; class cDarkElf : public cUnit { IState* _pState; void ChangeState(IState*); public: cDarkElf(); ~cDarkElf(); void Attack(cUnit* Target); void Move(int x, int y); void Stop(); void HoldPosition(); void Slaughter(cUnit* Target); void Update(); };
Class Definitions for State objects
class IState { const ID_STATE _ID; public: enum ID_STATE {STATE_ATTACK, STATE_MOVE, STATE_STOP, STATE_HOLDPOSITION, STATE_SLAUGHTER}; IState(ID_STATE state) : _ID(state) {} ID_STATE GetID() {return _ID;} virtual void Operate() = 0; }; class cState_Attack : public IState { cUnit* _pTarget; cUnit* _pUnit; public: cState_Attack(cUnit* target, cUnit* pUnit) : IState(STATE_ATTACK), _pTarget(target), _pUnit(pUnit){} void Operate(); }; class cState_Move : public IState { int _x, _y; cUnit* _pUnit; public: cState_Move(int x, int y, cUnit* pUnit); void Operate(); }; class cState_Stop : public IState { public: void Operate(); }; class cState_HoldPosition : public IState { public: void Operate(); }; class cState_SLAUGHTER : public IState { cUnit* _pTarget; cUnit* _pUnit; public: cState_Slaughter(cUnit* target, cUnit* pUnit) : IState(STATE_SLAUGHTER), _pTarget(target), _pUnit(pUnit) {} void Operate(); };
Functions that changes character status
cDarkElf::cDarkElf() {_pState = new cState_Stop;} cDarkElf::~cDarkElf() {delete _pState;} void cDarkElf::ChangeState(IState* pState) { delete _pState; _pState = pState; } void cDarkElf::Attack(cUnit* Target) { ChangeState(new cState_Attack(Target, this)); } void cDarkElf::Move(int x, int y) { ChangeState(new cState_Move(x, y, this)); } void cDarkElf::Stop() { ChangeState(new cState_Stop); } void cDarkElf::HoldPosition() { ChangeState(new cState_HoldPosition); } void cDarkElf::Patrol(int x1, int y1, int x2, int y2) { ChangeState(new cState_Patrol(x1, y1, x2, y2)); } void cDarkElf::Update() { _pState->Operate(); }
State Pattern Example: C#
Abstract Class
using System; using System.Drawing; namespace StatePatternApp { public abstract class State { public State() { } public State(State state) { this.CurrentLevel = state.CurrentLevel; this.Deviation = state.Deviation; this.MaxLevel = state.MaxLevel; this.MinLevel = state.MinLevel; this.Machine = state.Machine; this.SetParams(); } public State(Machine machine, int iCurrentLevel, int iDeviation, int iMaxLevel, int iMinLevel) { this.CurrentLevel = iCurrentLevel; this.Deviation = iDeviation; this.MaxLevel = iMaxLevel; this.MinLevel = iMinLevel; this.Machine = machine; this.SetParams(); } private Machine machine; private int minLevel = 0; private int maxLevel = 0; private int currentLevel = 0; private int deviation = 0; private Color messageColor = Color.Green; private string messageText = ""; public virtual void SetParams(){} public virtual void CheckEfficiency() { Transition t = Transition.GetInstance(); t.Transform(this); } protected virtual void ChangeState(Machine m, State s) { m.ChangeState(s); } public Machine Machine { get { return this.machine; } set { this.machine = value; } } public int MinLevel { get { return this.minLevel; } set { this.minLevel = value; } } public int MaxLevel { get { return this.maxLevel; } set { this.maxLevel = value; } } public int CurrentLevel { get { return this.currentLevel; } set { this.currentLevel = value; // Is the machine value set? if(this.Machine != null) { this.CheckEfficiency(); } } } public int Deviation { get { return this.deviation; } set { this.deviation = value; } } public int MaxDeviaton { get { return this.MaxLevel - this.Deviation; } } public int MinDeviaton { get { return this.MinLevel + this.Deviation; } } public Color MessageColor { get { return this.messageColor; } set { this.messageColor = value; } } public string MessageText { get { return this.messageText; } set { this.messageText = value; } } } }
Transition class
using System; namespace StatePatternApp { class Transition { private static Transition getInstance; protected Transition() {} public static Transition GetInstance() { if(getInstance == null) { getInstance = new Transition(); } return getInstance; } public void Transform(State state) { if(state == null) { return; } // Get the type of state. string stateType = state.GetType().Name; // Are we in normal state? if(state.CurrentLevel < state.MaxDeviaton && state.CurrentLevel > state.MinDeviaton) { if(stateType.ToUpper() != "NORMALSTATE") { state.ChangeState(state.Machine, new NormalState(state)); } } // Are we in warning? if(state.Deviation > 0) { if((state.CurrentLevel < state.MaxLevel && state.CurrentLevel >= state.MaxDeviaton) || state.CurrentLevel > state.MinLevel && state.CurrentLevel <= state.MinDeviaton) { if(stateType.ToUpper() != "WARNINGSTATE") { state.ChangeState(state.Machine, new WarningState(state)); } } } // Are we in alert state? if(state.CurrentLevel >= state.MaxLevel || state.CurrentLevel <= state.MinLevel) { if(stateType.ToUpper() != "ALERTSTATE") { state.ChangeState(state.Machine, new AlertState(state)); } } } } }
NormalState - derived state class
using System; using System.Drawing; namespace StatePatternApp { public class NormalState : State { public NormalState(State state) : base(state) { } public NormalState(Machine machine, int iCurrentLevel, int iDeviation, int iMaxLevel, int iMinLevel) : base(machine, iCurrentLevel, iDeviation, iMaxLevel, iMinLevel) { } public override void SetParams() { this.MessageColor = Color.Green; this.MessageText = "Normal"; } } }
WarningState - derived state class
using System; using System.Drawing; namespace StatePatternApp { public class WarningState : State { public WarningState(State state) : base(state) { } public WarningState(Machine machine, int iCurrentLevel, int iDeviation, int iMaxLevel, int iMinLevel) : base(machine, iCurrentLevel, iDeviation, iMaxLevel, iMinLevel) { } public override void SetParams() { this.MessageColor = Color.Yellow; this.MessageText = "Warning"; } } }
AlertState - derived state class
using System; using System.Drawing; namespace StatePatternApp { public class AlertState : State { public AlertState(State state) : base(state) { } public AlertState(Machine machine, int iCurrentLevel, int iDeviation, int iMaxLevel, int iMinLevel) : base(machine, iCurrentLevel, iDeviation, iMaxLevel, iMinLevel) { } public override void SetParams() { this.MessageColor = Color.Red; this.MessageText = "Alert"; } } }
Machine class - This class maintains an instance of state
using System;
namespace StatePatternApp
{
public class Machine
{
public State currentState;
public Machine(int iCurrentLevel, int iDeviation,
int iMaxLevel, int iMinLevel)
{
currentState = new NormalState(this, iCurrentLevel,
iDeviation, iMaxLevel, iMinLevel);
currentState.CheckEfficiency();
}
public void ChangeState(State setState)
{
currentState = setState;
}
public void SetCurrentLevel(int level)
{
currentState.CurrentLevel = level;
}
}
}
References
- Erich, Gamma. Design Patterns : elements of reusable object-oriented software. Upper Saddle River: Pearson Education Corporate Sales Division, 2000.
- "4th Lecture about Design Patterns". Ssamdark's Homepage. March 20, 2007 <http://www.misofruit.co.kr/seojewoo/programming/designpattern4.htm>.
- Young, Kenny. "C# - State Pattern Example". The Code Project. April 2nd, 2007 <http://www.codeproject.com/cs/design/statepatterncsharp.asp>.