設計模式--狀態與策略模式

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 應用場合
  3. 如果在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式可以動態地讓一個對象在許多行爲中選擇一種行爲。
  4. 一個系統需要動態地在幾種算法中選擇一種。
  5. 如果一個對象有很多的行爲,如果不用恰當的模式,這些行爲就只好使用多重的條件選擇語句來實現。
  6. 不希望客戶端知道複雜的、與算法相關的數據結構,在具體策略類中封裝算法和相關的數據結構,提高算法的保密性與安全性。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章