设计模式---状态变化模式之state状态模式(State)
前提:状态变化模式
在组建构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?“状态变化”模式为这一个问题提供了一种解决方案。
典型模式
状态模式:State
备忘录模式:Memento
一:State状态模式
(一)概念
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类
状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。
(二)动机
在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能会完全不同。
如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转化之间引入紧耦合?
我们将状态逻辑和动态实现进行分离操作
(三)代码讲解
1.原代码
enum NetworkState //状态枚举 { Network_Open, Network_Close, Network_Connect, }; class NetworkProcessor{ NetworkState state; public: void Operation1(){ //多种操作模式 if (state == Network_Open){ //根据当前网络状态,修改下一步的网络状态 //********** state = Network_Close; } else if (state == Network_Close){ //.......... state = Network_Connect; } else if (state == Network_Connect){ //$$$$$$$$$$ state = Network_Open; } } public void Operation2(){ if (state == Network_Open){ //********** state = Network_Connect; } else if (state == Network_Close){ //..... state = Network_Open; } else if (state == Network_Connect){ //$$$$$$$$$$ state = Network_Close; } } public void Operation3(){ } };
我们发现if...else..的使用同strategy策略模式十分相像,动态考虑,以后这些状态会不会出现变化,例如:出现Network_wait状态后如何变化?
违背了开闭原则,依循strategy模式经验,抽象基类提出
2.改进代码,将基类抽出
class NetworkState{ public: NetworkState* pNext; virtual void Operation1()=0; virtual void Operation2()=0; virtual void Operation3()=0; virtual ~NetworkState(){} };
class OpenState :public NetworkState{ static NetworkState* m_instance; //单例模式,因为各个状态只需要一个 public: static NetworkState* getInstance(){ if (m_instance == nullptr) { m_instance = new OpenState(); } return m_instance; } void Operation1(){ //********** 处理逻辑和上面还是一致的 pNext = CloseState::getInstance(); } void Operation2(){ //.......... pNext = ConnectState::getInstance(); } void Operation3(){ //$$$$$$$$$$ pNext = OpenState::getInstance(); } };
class CloseState:public NetworkState{ } //...多个状态对象
把状态相关的操作,全部编码到一个状态对象中
class NetworkProcessor{ NetworkState* pState; //放的不再是枚举类型,而是状态对象 public: NetworkProcessor(NetworkState* pState){ this->pState = pState; } void Operation1(){ //... pState->Operation1(); pState = pState->pNext; //... } void Operation2(){ //... pState->Operation2(); pState = pState->pNext; //... } void Operation3(){ //... pState->Operation3(); pState = pState->pNext; //... } };
是哪种状态就调用哪种操作
我们只需要关心状态自身的变化,而不需要去看状态和NetworkProcessor之间的关系
//扩展,解耦合 class WaitState:public NetworkState{ }
(四)模式定义
允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。
——《设计模式》GoF
(五)类图(结构)
state和strategy模式非常像,策略模式是一个行为,状态模式可以是一个也可以是多个行为,当是一个行为是和策略模式基本相同
(六)要点总结
1.State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时, 切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
只需要关心操作之后的下一个状态即可,而不需要去想更多耦合情况
2.为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的——即要么彻底转换过来,要么不转换。
3.如果State对象没有实例变量,那么各个上下文可以共享同一个State对象,从而节省对象开销。
单例模式,节省开销
(七)案例演示 (一)
1.state基类
class State { public: State* pNext; virtual void doSomething() = 0; virtual ~State(){} };
2.具体状态类
class Study :public State { private: Study(){} Study(const Study& obj){} Study& operator=(const Study& obj){} public: static State* m_instance; static State* getInstance() { if (m_instance == nullptr) { m_instance = new Study(); } return m_instance; } virtual void doSomething(); }; class EatFood :public State { private: EatFood(){} EatFood(const EatFood& obj){} EatFood& operator=(const EatFood& obj){} public: static State* m_instance; static State* getInstance() { if (m_instance == nullptr) { m_instance = new EatFood(); } return m_instance; } virtual void doSomething(); }; class Sleep :public State { private: Sleep(){} Sleep(const Sleep& obj){} Sleep& operator=(const Sleep& obj){} public: static State* m_instance; static State* getInstance() { if (m_instance == nullptr) { m_instance = new Sleep(); } return m_instance; } virtual void doSomething(); }; State* Study::m_instance = NULL; //静态成员必须在外部声明 State* Sleep::m_instance = NULL; State* EatFood::m_instance = NULL;
//因为存在各个类之间想回调用,所以将该函数定义写在所有类声明后面
void Study::doSomething() { cout << "I will to study" << endl; pNext = Sleep::getInstance(); } void Sleep::doSomething() { cout << "I will sleep" << endl; pNext = EatFood::getInstance(); } void EatFood::doSomething() { cout << "I will eat food" << endl; pNext = Study::getInstance(); }
3.客户实现
class Student { State* pState; public: Student(State* ps) :pState(ps){} void dosSomething() { pState->doSomething(); pState = pState->pNext; } };
4.状态演示
int main() { State* st = Sleep::getInstance(); Student* stu = new Student(st); stu->dosSomething(); stu->dosSomething(); stu->dosSomething(); system("pause"); return 0; }
(八)案例演示(二)
#include <iostream> #include <string> #include "state.h" using namespace std; #include <iostream> using namespace std; class Worker; class State { public: virtual void doSomeThing(Worker* w) = 0; }; class Worker { private: int m_hour; State* m_currState; //对象当前状态 public: Worker(); void setHour(int hour) { m_hour = hour; } int getHour() { return m_hour; } State* getCurrState() { return m_currState; } void setCurrentState(State* state) { m_currState = state; } void doSomeThing() { m_currState->doSomeThing(this); } }; class State1 :public State { public: static State* m_Instance; static State* getInstance() { if (m_Instance == NULL) { m_Instance = new State1(); } return m_Instance; } void doSomeThing(Worker* w); }; class State2 :public State { public: static State* m_Instance; static State* getInstance() { if (m_Instance==NULL) { m_Instance = new State2(); } return m_Instance; } void doSomeThing(Worker* w); }; State* State1::m_Instance = NULL; State* State2::m_Instance = NULL; void State1::doSomeThing(Worker* w) { if (w->getHour() == 7 || w->getHour() == 8) { cout << "eat food" << endl; } else { w->setCurrentState(State2::getInstance()); w->getCurrState()->doSomeThing(w); } } void State2::doSomeThing(Worker* w) { if (w->getHour() == 9 || w->getHour() == 10) { cout << "work" << endl; } else { w->setCurrentState(State1::getInstance()); //恢复到初始状态 cout << "current time don`t know to do something" << endl; } } Worker::Worker() { m_currState = new State1(); } void main() { Worker* w1 = new Worker(); w1->setHour(7); w1->doSomeThing(); w1->setHour(9); w1->doSomeThing(); delete w1; system("pause"); return; }