设计模式--状态与策略模式

来源:互联网 发布:thinkphp商城源码 编辑:程序博客网 时间:2024/06/05 03:45

1、来由

  • 最近要把与PC间的通信由原来的自定义的Message改为MQTT的通信方式,QMQTT也有一个数据结构是Message,所以要涉及到把自定义的Message转换为MQTT的Message的需求。每一个自定义Message的转换都不相同,所以想到了可以使用策略模式来处理,一直以来对于策略模式与状态模式都不是很了解,最近的一次是在看《重构》时的第一个例子时作者用到了策略模式,所以想把这两个模式再理一次。

2、Message的转换策略

  • 策略模式是围绕可以互换的算法来创建业务的,对于每一个自定义Message的转换算法都相互独立的。每一个Message 都代表着一个功能,所以有id号,而对于每一种转换,都可以有一个转换的类,id号与转换类形成了一一对应的关系。

2.1 UML图:

这里写图片描述

2.2 策略类实现

//策略基类class CMsgConvert{public:    CMsgConvert(int id);    virtual QMQTT::Message doconvert(const Message&) = 0;};//Message A 的转换类class CMsgAConvert :public CMsgConvert{public:    CMsgAConvert (int id);    virtual QMQTT::Message doconvert(const Message&);};//Message B 的转换类class CMsgBConvert :public CMsgConvert{public:    CMsgBConvert (int id);    virtual QMQTT::Message doconvert(const Message&);};....这里我们可以定义更多的转换策略子类

2.3 context 类实现

在我们的策略上下文类中使用一个map将自定义的Message的ID号与转换的子类形成一个映射。

class Context{public:    Context();    QMQTT::Message doconvert(const Message&);private:map<int,CMsgConvert*> mapConvert;}; Context::Context(){    //对MAP进行初始化    mapConvert.set(key,value);}QMQTT::Message Context::doconvert(const Message& msg){    //转换    if(mapConvert.contant(msg.id))    {        return mapConvert[msg.id].doconvert(msg);    }}

3、策略模式总结

策略模式相来说比较容易理解。

3.1 优点

  1. 策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。
  2. 在策略模式中,应当由客户端自己决定在什么情况下使用什么具体策略角色。
  3. 使用策略模式可以避免使用 if…else 和swtich…case
  4. 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。

3.2 缺点

  1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  2. 策略模式将造成产生很多策略类。

3.3 应用场合

  1. 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
  2. 一个系统需要动态地在几种算法中选择一种。
  3. 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
  4. 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。

4、双胞胎兄弟— 状态模式

  • 状态模式与状态模式是双胞胎,在出生的时候才分开,状态模式是通过改变内部的状态来帮助对象控制自已的行为的。
  • 网上有很多的文章在说状态模式与策略模式的区别,也很乱,以我的理解:
    状态模式强调的自己的状态的改变,而这种改变不是用户来改变的,决定权不在context类(策略模式是由context 类开决定的),而在于业务的本身。

或者说:这种状态改变可以用状态机来描述

4.1、UML图

这里写图片描述

在state类里拥有context 的引用,可以在handle的接口内调用context的changeState方法来更新状态。

4.2 Head First 例子—-万能糖果机

状态转移图
这里写图片描述

4.3 C++ 代码

4.3.1状态基类:

enum{    EGumMachineState_NoQuarter,    EGumMachineState_HasQuarter,    EGumMachineState_Sold,    EGumMachineState_SoldOut,    EGumMachineState_Winner,    EGumMachineState_Total,};//枚举所有状态class State{public:    State(GumballMachine *p);    virtual ~State(void){}public:    virtual void insertQuarter(void)=0;    virtual void ejectQuarter(void)=0;    virtual void turnCrank(void)=0;    virtual void dispense(void)=0;protected:    GumballMachine *pMachine;};---------------------------cpp--------------------------------State::State(GumballMachine *p)    :pMachine(p){}

4.3.2 无25美分的状态类

class NoQuarterState :public State{public:    NoQuarterState (GumballMachine *p);    ~NoQuarterState (void){}public:    virtual void insertQuarter(void);    virtual void ejectQuarter(void);    virtual void turnCrank(void);    virtual void dispense(void);};/******************************cpp***************************/NoQuarterState ::NoQuarterState (GumballMachine *p)    :State(p){}void NoQuarterState::insertQuarter(){    qDebug()<<"You inserted a quarter"<<endl;    pMachine->setState(pMachine->getState(EGumMachineState_HasQuarter));}void NoQuarterState::ejectQuarter(){    qDebug()<<"You haven't inserted a quarter"<<endl;}void NoQuarterState::turnCrank(){    qDebug()<<"You turned, but there's no quarter"<<endl;}void NoQuarterState::dispense(){    qDebug()<<"You need to pay first"<<endl;}

4.3.3 有25美分的状态类

class HasQuarterState  :public State{public:    HasQuarterState  (GumballMachine *p);    ~HasQuarterState  (void){}public:    virtual void insertQuarter(void);    virtual void ejectQuarter(void);    virtual void turnCrank(void);    virtual void dispense(void);};/******************************cpp***************************/HasQuarterState  ::HasQuarterState  (GumballMachine *p)    :State(p){}void HasQuarterState ::insertQuarter(){    qDebug()<<"You can't insert another quarter"<<endl;}void HasQuarterState ::ejectQuarter(){    qDebug()<<"Quarter returned"<<endl;    pMachine->setState(pMachine->getState(EGumMachineState_NoQuarter));}void HasQuarterState ::turnCrank(){    qDebug()<<"You turned..."<<endl;    int winner = rand()%10; // 产生随机数    qDebug()<<winner<<endl;    if((winner == 3) && (pMachine->getCount()>1))    {        pMachine->setState(pMachine->getState(EGumMachineState_Winner));    }    else    {        pMachine->setState(pMachine->getState(EGumMachineState_Sold));    }}void HasQuarterState ::dispense(){    qDebug()<<"No gumball dispensed"<<endl;} 

4.3.4 售出糖果状态

class SoldState   :public State{public:    SoldState   (GumballMachine *p);    ~SoldState   (void){}public:    virtual void insertQuarter(void);    virtual void ejectQuarter(void);    virtual void turnCrank(void);    virtual void dispense(void);};/******************************cpp***************************/SoldState::SoldState(GumballMachine *p)    :State(p){}void SoldState::insertQuarter(){    qDebug()<<"Please wait, we're already giving you a gumball"<<endl;}void SoldState  ::ejectQuarter(){   qDebug()<<"Sorry, you already turned the Crank"<<endl;}void SoldState::turnCrank(){    qDebug()<<"Turning twice doesn't get you another gumball!"<<endl;}void SoldState::dispense(){    pMachine->releaseGumball();    if(pMachine->getCount()>0)    {        pMachine->setState(pMachine->getState(EGumMachineState_NoQuarter));    }    else    {        qDebug()<<"Oops, out of gumballs!"<<endl;        pMachine->setState(pMachine->getState(EGumMachineState_SoldOut));    }} 

4.3.5 售罄状态

class SoldOutState    :public State{public:    SoldOutState    (GumballMachine *p);    ~SoldOutState    (void){}public:    virtual void insertQuarter(void);    virtual void ejectQuarter(void);    virtual void turnCrank(void);    virtual void dispense(void);};/******************************cpp***************************/SoldOutState ::SoldOutState (GumballMachine *p)    :State(p){}void SoldOutState::insertQuarter(){    qDebug()<<"You can't insert a quarter, the machine is sold out"<<endl;}void SoldOutState::ejectQuarter(){    qDebug()<<"You can't eject, you haven't inserted a quarter yet"<<endl;}void SoldOutState::turnCrank(){    qDebug()<<"You turned, but there are no gumballs"<<endl;}void SoldOutState::dispense(){    qDebug()<<"No gumball dispensed"<<endl;} 

4.3.6 赢家状态

class WinnerState:public State{public:    WinnerState(GumballMachine *p);    ~WinnerState(void){}public:    virtual void insertQuarter(void);    virtual void ejectQuarter(void);    virtual void turnCrank(void);    virtual void dispense(void);};/******************************cpp***************************/WinnerState::WinnerState (GumballMachine *p)    :State(p){}void WinnerState::insertQuarter(){    qDebug()<<"Please wait, we're already giving you a gumball"<<endl;}void WinnerState::ejectQuarter(){    qDebug()<<"Sorry, you already turned the Crank"<<endl;}void WinnerState::turnCrank(){    qDebug()<<"Turning twice doesn't get you another gumball!"<<endl;}void WinnerState::dispense(){    qDebug()<<"You're a Winner! You get two gumballs for your quarter";    pMachine->releaseGumball();    if(pMachine->getCount() == 0)    {       pMachine->setState(pMachine->getState(EGumMachineState_SoldOut));    }    else    {       pMachine->releaseGumball();       if(pMachine->getCount()>0)       {           pMachine->setState(pMachine->getState(EGumMachineState_NoQuarter));       }       else       {           qDebug()<<"Oops, out of gumballs!"<<endl;           pMachine->setState(pMachine->getState(EGumMachineState_SoldOut));       }    }}

4.3.7 Gum Machine

class State;class GumballMachine{public:    GumballMachine(int numberGumballs=0);    ~GumballMachine(void);public:    // 投入25美分    void insertQuarter(void);    // 退回25美分    void ejectQuarter(void);    // 转动曲柄    void turnCrank(void);public:    void releaseGumball(void);    void setState(State* pState);    int getCount(void);    State *getState(int);private:    int iCount;    State* pCurState;    QVector<State*> vecState;};/******************************cpp***************************/GumballMachine::GumballMachine(int numberGumballs)    :iCount(numberGumballs){    vecState.resize(EGumMachineState_Total);    vecState[EGumMachineState_NoQuarter] =  new NoQuarterState(this);    vecState[EGumMachineState_HasQuarter] = new HasQuarterState(this);    vecState[EGumMachineState_Sold] =       new SoldState(this);    vecState[EGumMachineState_SoldOut] =    new SoldOutState(this);    vecState[EGumMachineState_Winner] =     new WinnerState(this);    pCurState = vecState[EGumMachineState_SoldOut];    if(numberGumballs>0)    {       pCurState = vecState[EGumMachineState_NoQuarter];    }}GumballMachine::~GumballMachine(){    for(int i = 0 ; i < EGumMachineState_SoldOut;i++)    {      State *state = vecState[i];      delete state;    }}void GumballMachine::insertQuarter(){    pCurState->insertQuarter();}void GumballMachine::ejectQuarter(){    pCurState->ejectQuarter();}void GumballMachine::turnCrank(){    pCurState->turnCrank();    pCurState->dispense();}void GumballMachine::releaseGumball(){    if(iCount)    {        iCount--;    }}void GumballMachine::setState(State *pState){    pCurState = pState;}int GumballMachine::getCount(){    return iCount;}State *GumballMachine::getState(int index){    return vecState[index];}

4.3.8 Client

    GumballMachine *pGumballMachine = new GumballMachine(5);    pGumballMachine->insertQuarter();//    pGumballMachine->ejectQuarter();    pGumballMachine->turnCrank();    qDebug()<<"the gumball count is:"<<pGumballMachine->getCount()<<endl;

5、资料

https://en.wikibooks.org/wiki/C%2B%2B_Programming/Code/Design_Patterns#State
http://www.oodesign.com/strategy-pattern.html
http://blog.csdn.net/turkeyzhou/article/details/2792840
http://blog.csdn.net/hguisu/article/details/7558249/
http://blog.csdn.net/u010191243/article/details/45395787
http://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/state.html
http://blog.csdn.net/ccf19881030/article/details/8257659
http://www.cnblogs.com/Mainz/archive/2007/12/15/996081.html

原创粉丝点击