什么是状态?
状态即事物所处的某一种形态。状态模式是说一个对象在其内部状态发生改变时,其表现的行为和外在属性不一样,这个对象看上去就像是改变了它的类型一样。因此,状态模式又称为对象的行为模式。
如我们生活中经常见到的水,就有三种不同状态冰、水、水蒸汽,三种状态所表现的外在性质完全不一样:1.冰,质坚硬,无流动性,表面光滑;2.水,具有流动性;3.水蒸汽,肉眼看不见,却存在于空气中,质轻。这三种状态特性是不相差巨大?简直就不像是同一种物质的,但事实却是不管它是在什么状态,其特里组成都是一样的,都是水分子(H2O)。
以水为例说状态模式
你还记得初中物理学书上的这副图吗?水有三种状态:固、液、气,三种状态表现出不同的特性和行为,它们之间的转换也伴随着一着热力学的现象。现在要用程序来模拟水的三种状态和相互转换,要如何实现呢?
水之三态
我们从对象的角度来考虑会有哪个类,首先不管它是什么状态始终是水(H2O),所以会有一个Water类;而它又有三种状态,我们可以定义三个状态类:SolidState、LiquidState、GaseousState;从SolidState、LiquidState、GaseousState这三个单词中我们会发现都有一个State后缀,于是我们会想它们之间是否有一些共性,能否提取出一个更抽象的类,这个类就是状态类(State)。这些类之间的关系大致如下:
水与状态之间的类图关系
Ok,我们已经知道了大概的关系,那就开始Coding实现吧,在实现的过程中不断完善……
Water.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#ifndef __WATER_H__ #define __WATER_H__ //=============================================================== class State; //=============================================================== class Water { public: Water(); Water(State* pState, const string& strName); ~Water(); public: //改变状态 bool ChangeState(State* pState); //设置温度 void SetTemperature(int nTemperature); //获取温度 int GetTemperature(); //升温,nStep升高的幅度 void RiseTemperature(int nStep); //降温,nStep降低的幅度 void ReduceTemperature(int nStep); //获取名称 string GetName(); //水的作用(功效) void DoWork(); private: int m_nTemperature; //水温 string m_strName; //名称 State* m_pState; //状态 }; #endif //__WATER_H__ |
State.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
#ifndef __STATE_H__ #define __STATE_H__ //=============================================================== class Water; //=============================================================== class State { public: virtual ~State() { } //某一状态下表现的行为,用途 virtual void DoWork(Water* pWater) = 0; virtual string GetStateName(); protected: State(const string& strName) : m_strStateName(strName){} string m_strStateName; }; class SolidState : public State { public: static SolidState* GetInstance(); virtual void DoWork(Water* pWater); private: SolidState(const string& strName) : State(strName) { } static SolidState* s_pSolidState; class CGarbo{ public: ~CGarbo() { if (SolidState::s_pSolidState != NULL) { delete SolidState::s_pSolidState; } } }; static CGarbo s_Carbo; }; class LiquidState : public State { public: static LiquidState* GetInstance(); virtual void DoWork(Water* pWater); private: LiquidState(const string& strName) : State(strName) { } static LiquidState* s_pLiquidState; class CGarbo{ public: ~CGarbo() { if (LiquidState::s_pLiquidState != NULL) { delete LiquidState::s_pLiquidState; } } }; static CGarbo s_Carbo; }; class GaseousState : public State { public: static GaseousState* GetInstance(); virtual void DoWork(Water* pWater); private: GaseousState(const string& strName) : State(strName){ } static GaseousState* s_pGaseousState; class CGarbo{ public: ~CGarbo() { if (GaseousState::s_pGaseousState != NULL) { delete GaseousState::s_pGaseousState; } } }; static CGarbo s_Carbo; }; #endif //__STATE_H__ |
Water.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
#include "stdafx.h" #include "Water.h" #include "State.h" Water::Water() : m_pState(NULL), m_strName("水(H2O)") { SetTemperature(25); } Water::Water( State* pState, const string& strName) : m_nTemperature(0), m_pState(pState), m_strName(strName) { if (m_pState->GetStateName().compare("固态") == 0) { m_nTemperature = -25; } else if (m_pState->GetStateName().compare("液态") == 0) { m_nTemperature = 25; } else if (m_pState->GetStateName().compare("气态") == 0) { m_nTemperature = 125; } } Water::~Water() { } bool Water::ChangeState( State* pState ) { if (!pState) { return false; } if (!m_pState) { cout << "初始化为" << pState->GetStateName() << endl; } else { cout << "由" << m_pState->GetStateName() << "变为" << pState->GetStateName() << endl; } m_pState = pState; return true; } void Water::SetTemperature( int nTemperature ) { m_nTemperature = nTemperature; if (m_nTemperature <= 0) { ChangeState(SolidState::GetInstance()); } else if (m_nTemperature > 0 && m_nTemperature <= 100) { ChangeState(LiquidState::GetInstance()); } else { ChangeState(GaseousState::GetInstance()); } } int Water::GetTemperature() { return m_nTemperature; } void Water::RiseTemperature( int nStep ) { SetTemperature(m_nTemperature + nStep); } void Water::ReduceTemperature( int nStep ) { SetTemperature(m_nTemperature - nStep); } std::string Water::GetName() { return m_strName; } void Water::DoWork() { if (m_pState) { m_pState->DoWork(this); } } |
State.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include "stdafx.h" #include "State.h" #include "Water.h" string State::GetStateName() { return m_strStateName; } //=============================================================== SolidState* SolidState::s_pSolidState = NULL; LiquidState* LiquidState::s_pLiquidState = NULL; GaseousState* GaseousState::s_pGaseousState = NULL; //=============================================================== SolidState* SolidState::GetInstance() { if (!s_pSolidState) { s_pSolidState = new SolidState("固态"); } return s_pSolidState; } void SolidState::DoWork( Water* pWater ) { cout << "我性格高冷,当前体温" << pWater->GetTemperature() << "摄氏度," << "我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿……" << endl; } LiquidState* LiquidState::GetInstance() { if (!s_pLiquidState) { s_pLiquidState = new LiquidState("液态"); } return s_pLiquidState; } void LiquidState::DoWork( Water* pWater ) { cout << "我性格温和,当前体温" << pWater->GetTemperature() << "摄氏度," << "我可滋润万物,饮用我可让你活力倍增……" << endl; } GaseousState* GaseousState::GetInstance() { if (!s_pGaseousState) { s_pGaseousState = new GaseousState("气态"); } return s_pGaseousState; } void GaseousState::DoWork( Water* pWater ) { cout << "我性格热烈,当前体温" << pWater->GetTemperature() << "摄氏度," << "飞向天空是我毕生的梦想,在这你将看不到我的存在,我将达到无我的境界……" << endl; } |
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void TestStateDesign() { Water water; water.DoWork(); water.SetTemperature(-4); water.DoWork(); water.RiseTemperature(8); water.DoWork(); water.RiseTemperature(110); water.DoWork(); water.ReduceTemperature(130); water.DoWork(); } |
结果如下:
1 2 3 4 5 6 7 8 9 10 11 |
初始化为液态 我性格温和,当前体温25摄氏度,我可滋润万物,饮用我可让你活力倍增…… 由液态变为固态 我性格高冷,当前体温-4摄氏度,我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿…… 由固态变为液态 我性格温和,当前体温4摄氏度,我可滋润万物,饮用我可让你活力倍增…… 由液态变为气态 我性格热烈,当前体温114摄氏度,飞向天空是我毕生的梦想,在这你将看不到我的存在, 我将达到无我的境界…… 由气态变为固态 我性格高冷,当前体温-16摄氏度,我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿…… |
好了,上面的程序已经完美地帮我们完成了水的三种状态及其转变的模拟。我们再来整理一个上面的代码中的类图结构,如下图:
水与状态之间的类图关系
SolidState、LiquidState、GaseousState三个类用了单例的模式,因为状态只需要一个对象就可以了,这三个类中CGarbo是一个内部类,只为了释放静态对象,关于单例模式的用法,可以参考《生活中的单例——不是单身》。Water中的ChangeState可方便地进行三种状态之间的转换。
水之三态升级版
在本篇文章发表之后,有读者提出说上面的例子并不太好:因为void Water::SetTemperature( int nTemperature )方法不符合程序设计中的开放封闭原则,如果再加一个状态(State),则要在SetTemperature中再一个if else判断。
这位读者说的确实有道理,这里的SetTemperature确实不符合开放封闭原则,在这要感谢[reply]FRcheng[/reply]提出了宝贵的意见和对此的看法。但我依然觉得水的三种状态是状态模式的典型,因为这特别贴近生活,一说就懂。会出现不符合开放封闭原则的情况是因为我程序写的完善,于是经过思考后又修改了一个新的版本,这版本即符合开发封闭原则,又满足了FRcheng提到的“状态可变化,也可增加”的需求。
State.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
#ifndef __STATE_H__ #define __STATE_H__ //=============================================================== class Water; //=============================================================== class State { public: virtual ~State() { } virtual string GetStateName(); //某一状态下表现的行为,用途 virtual void DoWork(Water* pWater) = 0; //返回这一状态下的默认温度 virtual int GetDefaultTemperature() = 0; //指定的温度是否符合当前的状态 virtual bool IsMatched(int nTemperature) = 0; protected: State(const string& strName) : m_strStateName(strName){} string m_strStateName; }; class SolidState : public State { public: static SolidState* GetInstance(); virtual void DoWork(Water* pWater); virtual int GetDefaultTemperature(); virtual bool IsMatched(int nTemperature); private: SolidState(const string& strName) : State(strName) { } static SolidState* s_pSolidState; class CGarbo{ public: ~CGarbo() { if (SolidState::s_pSolidState != NULL) { delete SolidState::s_pSolidState; } } }; static CGarbo s_Carbo; }; class LiquidState : public State { public: static LiquidState* GetInstance(); virtual void DoWork(Water* pWater); virtual int GetDefaultTemperature(); virtual bool IsMatched(int nTemperature); private: LiquidState(const string& strName) : State(strName) { } static LiquidState* s_pLiquidState; class CGarbo{ public: ~CGarbo() { if (LiquidState::s_pLiquidState != NULL) { delete LiquidState::s_pLiquidState; } } }; static CGarbo s_Carbo; }; class GaseousState : public State { public: static GaseousState* GetInstance(); virtual void DoWork(Water* pWater); virtual int GetDefaultTemperature(); virtual bool IsMatched(int nTemperature); private: GaseousState(const string& strName) : State(strName){ } static GaseousState* s_pGaseousState; class CGarbo{ public: ~CGarbo() { if (GaseousState::s_pGaseousState != NULL) { delete GaseousState::s_pGaseousState; } } }; static CGarbo s_Carbo; }; #endif //__STATE_H__ |
State.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
#include "stdafx.h" #include "State.h" #include "Water.h" string State::GetStateName() { return m_strStateName; } //=============================================================== SolidState* SolidState::s_pSolidState = NULL; LiquidState* LiquidState::s_pLiquidState = NULL; GaseousState* GaseousState::s_pGaseousState = NULL; //=============================================================== SolidState* SolidState::GetInstance() { if (!s_pSolidState) { s_pSolidState = new SolidState("固态"); } return s_pSolidState; } void SolidState::DoWork( Water* pWater ) { cout << "我性格高冷,当前体温" << pWater->GetTemperature() << "摄氏度," << "我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿……" << endl; } int SolidState::GetDefaultTemperature() { return -5; } bool SolidState::IsMatched( int nTemperature ) { return nTemperature < 0; } LiquidState* LiquidState::GetInstance() { if (!s_pLiquidState) { s_pLiquidState = new LiquidState("液态"); } return s_pLiquidState; } void LiquidState::DoWork( Water* pWater ) { cout << "我性格温和,当前体温" << pWater->GetTemperature() << "摄氏度," << "我可滋润万物,饮用我可让你活力倍增……" << endl; } int LiquidState::GetDefaultTemperature() { return 25; //常温 } bool LiquidState::IsMatched( int nTemperature ) { return nTemperature >= 0 && nTemperature < 100; } GaseousState* GaseousState::GetInstance() { if (!s_pGaseousState) { s_pGaseousState = new GaseousState("气态"); } return s_pGaseousState; } void GaseousState::DoWork( Water* pWater ) { cout << "我性格热烈,当前体温" << pWater->GetTemperature() << "摄氏度," << "飞向天空是我毕生的梦想,在这你将看不到我的存在,我将达到无我的境界……" << endl; } int GaseousState::GetDefaultTemperature() { return 105; } bool GaseousState::IsMatched( int nTemperature ) { return nTemperature >= 100; } |
Water.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#ifndef __WATER_H__ #define __WATER_H__ //=============================================================== class State; //=============================================================== #include <vector> //=============================================================== class Water { public: Water(); Water(State* pState, const string& strName); ~Water(); public: void AddState(State* pState); //改变状态 bool ChangeState(State* pState); //设置温度 void SetTemperature(int nTemperature); //获取温度 int GetTemperature(); //升温,nStep升高的幅度 void RiseTemperature(int nStep); //降温,nStep降低的幅度 void ReduceTemperature(int nStep); //获取名称 string GetName(); //水的作用(功效) void DoWork(); private: int m_nTemperature; //水温 string m_strName; //名称 State* m_pCurrentState; //当前状态 typedef vector<State*> StateVec; StateVec* m_pvecState; //所有可能的状态 }; #endif //__WATER_H__ |
Water.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
#include "stdafx.h" #include "Water.h" #include "State.h" #include <algorithm> Water::Water() : m_pCurrentState(LiquidState::GetInstance()), m_strName("水(H2O)"), m_pvecState(new StateVec()) { m_pvecState->push_back(m_pCurrentState); m_nTemperature = m_pCurrentState->GetDefaultTemperature(); } Water::Water( State* pState, const string& strName) : m_nTemperature(0), m_pCurrentState(pState), m_strName(strName), m_pvecState(new StateVec()) { m_pvecState->push_back(m_pCurrentState); m_nTemperature = m_pCurrentState->GetDefaultTemperature(); } Water::~Water() { m_pCurrentState = NULL; m_pvecState->clear(); delete m_pvecState; m_pvecState = NULL; } void Water::AddState( State* pState ) { StateVec::iterator itrResult = find(m_pvecState->begin(), m_pvecState->end(), pState); if (itrResult == m_pvecState->end()) { m_pvecState->push_back(pState); //如果不在状态列表中,则添加进状态列表 } } bool Water::ChangeState( State* pState ) { if (!pState) { return false; } if (!m_pCurrentState) { cout << "初始化为" << pState->GetStateName() << endl; } else { cout << "由" << m_pCurrentState->GetStateName() << "变为" << pState->GetStateName() << endl; } m_pCurrentState = pState; StateVec::iterator itrResult = find(m_pvecState->begin(), m_pvecState->end(), pState); if (itrResult == m_pvecState->end()) { m_pvecState->push_back(pState); //如果不在状态列表中,则添加进状态列表 } return true; } void Water::SetTemperature( int nTemperature ) { m_nTemperature = nTemperature; //如果此温度不符合当前的状态,则要更改状态 if (!m_pCurrentState->IsMatched(m_nTemperature)) { for (StateVec::iterator itr = m_pvecState->begin(); itr != m_pvecState->end(); ++ itr) { State* pState = *itr; if (pState->IsMatched(m_nTemperature)) { ChangeState(pState); } } } } int Water::GetTemperature() { return m_nTemperature; } void Water::RiseTemperature( int nStep ) { SetTemperature(m_nTemperature + nStep); } void Water::ReduceTemperature( int nStep ) { SetTemperature(m_nTemperature - nStep); } std::string Water::GetName() { return m_strName; } void Water::DoWork() { if (m_pCurrentState) { m_pCurrentState->DoWork(this); } } |
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void TestStateDesign() { Water water; water.AddState(SolidState::GetInstance()); water.AddState(LiquidState::GetInstance()); water.AddState(GaseousState::GetInstance()); water.DoWork(); water.SetTemperature(-4); water.DoWork(); water.RiseTemperature(8); water.DoWork(); water.RiseTemperature(110); water.DoWork(); water.ReduceTemperature(130); water.DoWork(); } |
结果:
1 2 3 4 5 6 7 8 9 10 |
我性格温和,当前体温25摄氏度,我可滋润万物,饮用我可让你活力倍增…… 由液态变为固态 我性格高冷,当前体温-4摄氏度,我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿…… 由固态变为液态 我性格温和,当前体温4摄氏度,我可滋润万物,饮用我可让你活力倍增…… 由液态变为气态 我性格热烈,当前体温114摄氏度,飞向天空是我毕生的梦想,在这你将看不到我的存在, 我将达到无我的境界…… 由气态变为固态 我性格高冷,当前体温-16摄氏度,我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿…… |
状态模式总结
通过上面一个生活中的例子,应该很容易能明白状态模式吧!其实也简单,其结构关系如下:
状态模式结构图
Behavior是不同一个对象在不同状态下的行为(如DoWork),ChangeState用于改变对象的状态,Request则是操作请求(如RiseTemperature和ReduceTemperature)。
发表评论
要发表评论,您必须先登录。