C++狀態模式詳解--設計模式(15)
State模式來源:
每個人、事物在不同的狀態下會有不同表現(動作),而一個狀態又會在不同的表現下轉移到下一個不同的狀態(State)。最簡單的一個生活中的例子就是:地鐵入口處,如果你放入正確的地鐵票,門就會開啟讓你通過。在出口處也是驗票,如果正確你就可以ok,否則就不讓你通過(如果你動作野蠻,或許會有報警(Alarm))。
有限狀態自動機(FSM)也是一個典型的狀態不同,對輸入有不同的響應(狀態轉移)。通常我們在實現這類系統會使用到很多的Switch/Case語句,Case某種狀態,發生什麼動作,Case另外一種狀態,則發生另外一種狀態。但是這種實現方式至少有以下兩個問題:
(1).當狀態數目不是很多的時候,Switch/Case可能可以搞定。但是當狀態數目很多的時候(實際系統中也正是如此),維護一大組的Switch/Case語句將是一件異常困難並且容易出錯的事情。
(2).狀態邏輯和動作實現沒有分離。在很多的系統實現中,動作的實現程式碼直接寫在狀態的邏輯當中。這帶來的後果就是系統的擴充套件性和維護得不到保證。
State模式作用:
State模式就是被用來解決上面列出的兩個問題的,在State模式中我們將狀態邏輯和動作實現進行分離。當一個操作中要維護大量的case分支語句,並且這些分支依賴於物件的狀態。State模式將每一個分支都封裝到獨立的類中。
State模式UML結構圖如圖1所示:
State模式的構成:
State類:抽象狀態類,定義一個介面以封裝與Context的一個特定狀態相關的行為。
ConcreteState類:具體狀態,每一個子類實現一個與Context的一個狀態相關的行為。
Context類:維護一個ConcreteState子類的例項,這個例項定義當前的狀態。
State模式的程式碼示例:
State.h
#ifndef _STATE_H_ #define _STATE_H_ class Context; class State { public: virtual void Handle(Context* pContext)=0; ~State(); protected: State(); private: }; class ConcreteStateA : public State { public: ConcreteStateA(); ~ConcreteStateA(); virtual void Handle(Context* pContext); protected: private: }; class ConcreteStateB : public State { public: ConcreteStateB(); ~ConcreteStateB(); virtual void Handle(Context* pContext); protected: private: }; class ConcreteStateC : public State { public: ConcreteStateC(); ~ConcreteStateC(); virtual void Handle(Context* pContext); protected: private: }; class Context { public: Context(State* pState); ~Context(); void Request(); void ChangeState(State* pState); protected: private: State* _state; }; #endif
State.cpp
#include "State.h"
#include <iostream>
using namespace std;
State::State()
{}
State::~State()
{}
ConcreteStateA::ConcreteStateA()
{}
ConcreteStateA::~ConcreteStateA()
{}
//執行該狀態的行為並改變狀態
void ConcreteStateA::Handle(Context* pContext)
{
cout << "ConcreteStateA" << endl;
pContext->ChangeState(new ConcreteStateB());
}
ConcreteStateB::ConcreteStateB()
{}
ConcreteStateB::~ConcreteStateB()
{}
//執行該狀態的行為並改變狀態
void ConcreteStateB::Handle(Context* pContext)
{
cout << "ConcreteStateB" << endl;
pContext->ChangeState(new ConcreteStateC());
}
ConcreteStateC::ConcreteStateC()
{}
ConcreteStateC::~ConcreteStateC()
{}
//執行該狀態的行為並改變狀態
void ConcreteStateC::Handle(Context* pContext)
{
cout << "ConcreteStateC" << endl;
pContext->ChangeState(new ConcreteStateA());
}
//定義_state的初始狀態
Context::Context(State* pState)
{
this->_state = pState;
}
Context::~Context()
{}
//對請求做處理,並設定下一狀態
void Context::Request()
{
if(NULL != this->_state)
{
this->_state->Handle(this);
}
}
//改變狀態
void Context::ChangeState(State* pState)
{
this->_state = pState;
}
Main.cpp#include "State.h"
int main()
{
State* pState = new ConcreteStateA();
Context* pContext = new Context(pState);
pContext->Request();
pContext->Request();
pContext->Request();
pContext->Request();
pContext->Request();
return 0;
}
State模式使用場景:
State模式的應用也非常廣泛,從最高層邏輯使用者介面GUI到最底層的通訊協議(例如利用State模式模擬實現一個TCP連線的類。)都有其用武之地。
State模式和Strategy模式又很大程度上的相似:它們都有一個Context類,都是通過委託(組合)給一個具有多個派生類的多型基類實現Context的演算法邏輯。兩者最大的差別就是State模式中派生類持有指向Context物件的引用,並通過這個引用呼叫Context中的方法,但在Strategy模式中就沒有這種情況。因此可以說一個State例項同樣是Strategy模式的一個例項,反之卻不成立。實際上State模式和Strategy模式的區別還在於它們所關注的點不盡相同:State模式主要是要適應物件對於狀態改變時的不同處理策略的實現,而Strategy則主要是具體演算法和實現介面的解耦(coupling),Strategy模式中並沒有狀態的概念(雖然很多時候有可以被看作是狀態的概念),並且更加不關心狀態的改變了。
State模式很好地實現了物件的狀態邏輯和動作實現的分離,狀態邏輯分佈在State的派生類中實現,而動作實現則可以放在Context類中實現(這也是為什麼State派生類需要擁有一個指向Context的指標)。這使得兩者的變化相互獨立,改變State的狀態邏輯可以很容易複用Context的動作,也可以在不影響State派生類的前提下建立Context的子類來更改或替換動作實現。
State模式經典示例:
允許一個物件在其內部狀態改變時改變它的行為。物件看起來似乎修改了它的類。
1->main(),客戶
2->CLiftState,電梯狀態抽象類
3->CCloseingState,電梯門關閉
4->COpenningState,電梯門開啟
5->CRunningState,電梯執行
6->CStoppingState,電梯停止
7->CContext,電梯的控制面板
說明:CContext保持電梯的狀態,並提供操作的介面函式。當函式被呼叫時,CContext直接呼叫當前狀態的相應函式。由狀態的介面函式來確定是否可以執行這個動作,以及修改狀態為執行這個動作後的狀態。
看程式碼:第一塊是不使用模式的做法,第二塊是使用模式的做法,在main()函式裡會有呼叫的方式。
<span style="font-size:14px;"><span style="font-size:14px;">//ILift.h
#pragma once
class ILift
{
public:
ILift(void)
{
}
virtual ~ILift(void)
{
}
static const int OPENING_STATE = 1;
static const int CLOSING_STATE = 2;
static const int RUNNING_STATE = 3;
static const int STOPPING_STATE = 4;
virtual void SetState(int state) = 0;
virtual void Open() = 0;
virtual void Close() = 0;
virtual void Run() = 0;
virtual void Stop() = 0;
};
//Lift.h
#pragma once
#include "ilift.h"
class CLift :
public ILift
{
public:
CLift(void);
~CLift(void);
void SetState(int state);
void Open();
void Close();
void Run();
void Stop();
private:
int m_state;
void OpenWithoutLogic();
void CloseWithoutLogic();
void RunWithoutLogic();
void StopWithoutLogic();
};
//Lift.cpp
#include "StdAfx.h"
#include "Lift.h"
#include <iostream>
using std::cout;
using std::endl;
CLift::CLift(void)
{
this->m_state = 0;
}
CLift::~CLift(void)
{
}
void CLift::SetState(int state)
{
this->m_state = state;
}
void CLift::Open()
{
switch(this->m_state)
{
case OPENING_STATE:
break;
case CLOSING_STATE:
this->OpenWithoutLogic();
this->SetState(OPENING_STATE);
break;
case RUNNING_STATE:
break;
case STOPPING_STATE:
this->OpenWithoutLogic();
this->SetState(OPENING_STATE);
break;
}
}
void CLift::Close()
{
switch(this->m_state)
{
case OPENING_STATE:
this->CloseWithoutLogic();
this->SetState(CLOSING_STATE);
break;
case CLOSING_STATE:
break;
case RUNNING_STATE:
break;
case STOPPING_STATE:
break;
}
}
void CLift::Run()
{
switch(this->m_state)
{
case OPENING_STATE:
break;
case CLOSING_STATE:
this->RunWithoutLogic();
this->SetState(RUNNING_STATE);
break;
case RUNNING_STATE:
break;
case STOPPING_STATE:
this->RunWithoutLogic();
this->SetState(RUNNING_STATE);
break;
}
}
void CLift::Stop()
{
switch(this->m_state)
{
case OPENING_STATE:
break;
case CLOSING_STATE:
this->StopWithoutLogic();
this->SetState(CLOSING_STATE);
break;
case RUNNING_STATE:
this->StopWithoutLogic();
this->SetState(CLOSING_STATE);
break;
case STOPPING_STATE:
break;
}
}
void CLift::OpenWithoutLogic()
{
cout << "電梯門開啟..." << endl;
}
void CLift::CloseWithoutLogic()
{
cout << "電梯門關閉..." << endl;
}
void CLift::RunWithoutLogic()
{
cout << "電梯上下跑起來..." << endl;
}
void CLift::StopWithoutLogic()
{
cout << "電梯停止了..." << endl;
}</span></span>
<span style="font-size:14px;"><span style="font-size:14px;">//LiftState.h
#pragma once
class CContext;
class CLiftState
{
public:
CLiftState(void);
virtual ~CLiftState(void);
void SetContext(CContext *pContext);
virtual void Open() = 0;
virtual void Close() = 0;
virtual void Run() = 0;
virtual void Stop() = 0;
protected:
CContext *m_pContext;
};
//LiftState.cpp
#include "StdAfx.h"
#include "LiftState.h"
CLiftState::CLiftState(void)
{
}
CLiftState::~CLiftState(void)
{
}
void CLiftState::SetContext( CContext *pContext )
{
m_pContext = pContext;
}
//CloseingState.h
#pragma once
#include "liftstate.h"
class CCloseingState :
public CLiftState
{
public:
CCloseingState(void);
~CCloseingState(void);
void Open();
void Close();
void Run();
void Stop();
};
//CloseingState.cpp
#include "StdAfx.h"
#include "CloseingState.h"
#include "Context.h"
#include <iostream>
using std::cout;
using std::endl;
CCloseingState::CCloseingState(void)
{
}
CCloseingState::~CCloseingState(void)
{
}
void CCloseingState::Open()
{
this->CLiftState::m_pContext->SetLiftState(CContext::pOpenningState);
this->CLiftState::m_pContext->GetLiftState()->Open();
}
void CCloseingState::Close()
{
cout << "電梯門關閉..." << endl;
}
void CCloseingState::Run()
{
this->CLiftState::m_pContext->SetLiftState(CContext::pRunningState);
this->CLiftState::m_pContext->GetLiftState()->Run();
}
void CCloseingState::Stop()
{
this->CLiftState::m_pContext->SetLiftState(CContext::pStoppingState);
this->CLiftState::m_pContext->GetLiftState()->Stop();
}
//OpenningState.h
#pragma once
#include "liftstate.h"
class COpenningState :
public CLiftState
{
public:
COpenningState(void);
~COpenningState(void);
void Open();
void Close();
void Run();
void Stop();
};
//OpenningState.cpp
#include "StdAfx.h"
#include "OpenningState.h"
#include "Context.h"
#include <iostream>
using std::cout;
using std::endl;
COpenningState::COpenningState(void)
{
}
COpenningState::~COpenningState(void)
{
}
void COpenningState::Open()
{
cout << "電梯門開啟..." << endl;
}
void COpenningState::Close()
{
this->CLiftState::m_pContext->SetLiftState(CContext::pCloseingState);
this->CLiftState::m_pContext->GetLiftState()->Close();
}
void COpenningState::Run()
{
//do nothing
}
void COpenningState::Stop()
{
//do nothing
}
//RunningState.h
#pragma once
#include "liftstate.h"
class CRunningState :
public CLiftState
{
public:
CRunningState(void);
~CRunningState(void);
void Open();
void Close();
void Run();
void Stop();
};
//RunningState.cpp
#include "StdAfx.h"
#include "RunningState.h"
#include "Context.h"
#include <iostream>
using std::cout;
using std::endl;
CRunningState::CRunningState(void)
{
}
CRunningState::~CRunningState(void)
{
}
void CRunningState::Open()
{
//do nothing
}
void CRunningState::Close()
{
//do nothing
}
void CRunningState::Run()
{
cout << "電梯上下跑..." << endl;
}
void CRunningState::Stop()
{
this->CLiftState::m_pContext->SetLiftState(CContext::pStoppingState);
this->CLiftState::m_pContext->GetLiftState()->Stop();
}
//StoppingState.h
#pragma once
#include "liftstate.h"
class CStoppingState :
public CLiftState
{
public:
CStoppingState(void);
~CStoppingState(void);
void Open();
void Close();
void Run();
void Stop();
};
//StoppingState.cpp
#include "StdAfx.h"
#include "StoppingState.h"
#include "Context.h"
#include <iostream>
using std::cout;
using std::endl;
CStoppingState::CStoppingState(void)
{
}
CStoppingState::~CStoppingState(void)
{
}
void CStoppingState::Open()
{
this->CLiftState::m_pContext->SetLiftState(CContext::pOpenningState);
this->CLiftState::m_pContext->GetLiftState()->Open();
}
void CStoppingState::Close()
{
//do nothing
}
void CStoppingState::Run()
{
this->CLiftState::m_pContext->SetLiftState(CContext::pRunningState);
this->CLiftState::m_pContext->GetLiftState()->Run();
}
void CStoppingState::Stop()
{
cout << "電梯停止了..." << endl;
}</span></span>
<span style="font-size:14px;"><span style="font-size:14px;">//Contex.h
#pragma once
#include "LiftState.h"
#include "OpenningState.h"
#include "CloseingState.h"
#include "RunningState.h"
#include "StoppingState.h"
class CContext
{
public:
CContext(void);
~CContext(void);
static COpenningState *pOpenningState;
static CCloseingState *pCloseingState;
static CRunningState *pRunningState;
static CStoppingState *pStoppingState;
CLiftState * GetLiftState();
void SetLiftState(CLiftState *pLiftState);
void Open();
void Close();
void Run();
void Stop();
private:
CLiftState *m_pLiftState;
};
//Context.cpp
#include "StdAfx.h"
#include "Context.h"
COpenningState* CContext::pOpenningState = NULL;
CCloseingState* CContext::pCloseingState = NULL;
CRunningState* CContext::pRunningState = NULL;
CStoppingState* CContext::pStoppingState = NULL;
CContext::CContext(void)
{
m_pLiftState = NULL;
pOpenningState = new COpenningState();
pCloseingState = new CCloseingState();
pRunningState = new CRunningState();
pStoppingState = new CStoppingState();
}
CContext::~CContext(void)
{
delete pOpenningState;
pOpenningState = NULL;
delete pCloseingState;
pCloseingState = NULL;
delete pRunningState;
pRunningState = NULL;
delete pStoppingState;
pStoppingState = NULL;
}
CLiftState * CContext::GetLiftState()
{
return m_pLiftState;
}
void CContext::SetLiftState(CLiftState *pLiftState)
{
this->m_pLiftState = pLiftState;
this->m_pLiftState->SetContext(this);
}
void CContext::Open()
{
this->m_pLiftState->Open();
}
void CContext::Close()
{
this->m_pLiftState->Close();
}
void CContext::Run()
{
this->m_pLiftState->Run();
}
void CContext::Stop()
{
this->m_pLiftState->Stop();
}
// State.cpp : 定義控制檯應用程式的入口點。
#include "stdafx.h"
#include "ILift.h"
#include "Lift.h"
#include "Context.h"
#include "OpenningState.h"
#include "CloseingState.h"
#include "RunningState.h"
#include "StoppingState.h"
#include <iostream>
using std::cout;
using std::endl;
void DoIt()
{
//ILift.h, Lift.h, Lift.cpp
ILift *pLift = new CLift();
pLift->SetState(ILift::STOPPING_STATE);//電梯的初始條件是停止狀態。
pLift->Open();//首先是電梯門開啟,人進去
pLift->Close();//然後電梯門關閉
pLift->Run();//再然後,電梯跑起來,向上或者向下
pLift->Stop();//最後到達目的地,電梯停下來
delete pLift;
}
void DoNew()
{
//LiftState.h, LiftState.cpp, OpenningState.h, CloseingState.h, RunningState.h, StoppingState.h
//Context.h, Context.cpp
CContext context;
CCloseingState closeingState;
context.SetLiftState(&closeingState);
context.Close();
context.Open();
context.Run();
context.Stop();
}
int _tmain(int argc, _TCHAR* argv[])
{
cout << "----------使用模式之前----------" << endl;
DoIt();
cout << "----------使用模式之後----------" << endl;
DoNew();
_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF);
_CrtDumpMemoryLeaks();
return 0;
}</span></span>
State狀態模式的優點:(1).狀態模式將與特定狀態相關的行為區域性化,並且將不同狀態的行為分割開來。
(2).所有狀態相關的程式碼都存在於某個ConcereteState中,所以通過定義新的子類很容易地增加新的狀態和轉換。
(3).狀態模式通過把各種狀態轉移邏輯分不到State的子類之間,來減少相互間的依賴。
State狀態模式的缺點:
(1).在於使用狀態模式會增加系統類和物件的個數,且狀態模式的結構與實現都較為複雜,如果使用不當將導致程式結構和程式碼的混亂,對於可以切換狀態的狀態模式不滿足“開閉原則”的要求。導致較多的ConcreteState子類
State狀態模式使用總結:
策略模式關注行為的變化,但歸根結底只有一個行為,變化的只是行為的實現.客戶不關注這些.當新增變化時對客戶可以沒有任何影響.狀態模式同樣關注行為的變化,但這個變化是由狀態來驅動,一般來說每個狀態和行為都不同.新增的狀態或行為一般與已有的不同,客戶需要關注這些變化.狀態模式中State及其子類中的操作都將Context傳入作為引數,以便State可以通過這個指標呼叫Context中的方法,修改狀態。而策略模式沒有。