設計模式之狀態模式

一、狀態模式的概念

狀態模式屬於行爲型設計模式,它通過改變對象的內部狀態而改變對象的行爲,這個對象表現得就好像修改了它的類一樣。

狀態模式主要解決:當控制一個對象狀態轉換的條件表達式過於複雜時,把狀態的判斷邏輯轉移到表現不同狀態的一系列類當中,從而使複雜的判斷邏輯簡單化。

二、狀態模式使用場景

1、行爲隨狀態改變而改變的場景。
2、條件和分支語句的替代者。

三、狀態模式構建方法

1、用戶對象(上下文環境)類(Context)
擁有一個狀態類型的成員,以標識對象的當前狀態,並提供給客戶端調用的接口。

2、抽象狀態類(State)
封裝與用戶對象的特定狀態相關行爲的接口和方法。

3、具體狀態類(ConcreteState)
實現抽象狀態類定義的接口和方法。

四、狀態模式的示例

// StatePattern.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//

#include <iostream>
#include <string>

#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}

using namespace std;

// 根據時間點不同模仿一個人吃早飯、午飯、晚飯的狀態,其他時間狀態都設置在工作
class EatMeal;
//class MorningTimeState;
//class NoonTimeState;
//class NightTimeState;

// 抽象狀態類(State)
class TimeState
{
public:
	virtual void changeTimeState(int timeState) = 0;
	virtual void toEatMeal(EatMeal *pEatMeal) = 0;
};

// 用戶對象(上下文環境)類(Context)-去吃飯
class EatMeal
{
public:
	EatMeal()
	{
		m_curTimeState = nullptr;
	}

	~EatMeal()
	{
		DELETE_PTR(m_curTimeState);
	}

	void changeTimeState(int timeState)
	{
		m_timeState = timeState;
	}

	int getCurTime()
	{
		return m_timeState;
	}

	void setTimeState(TimeState *pCurTimeState)
	{
		m_curTimeState = pCurTimeState;
	}

	TimeState *getTimeState()
	{
		return m_curTimeState;
	}

	void toEatMeal()
	{
		m_curTimeState->toEatMeal(this);
	}

private:
	int m_timeState;
	TimeState *m_curTimeState;
};

// 具體狀態類(ConcreteState)-吃早飯
class MorningTimeState : public TimeState
{
public:
	void changeTimeState(int timeState)
	{
		m_timeState = timeState;
	}

	virtual void toEatMeal(EatMeal *pEatMeal);
private:
	int m_timeState;
};

// 具體狀態類(ConcreteState)-吃午飯
class NoonTimeState : public TimeState
{
public:
	void changeTimeState(int timeState)
	{
		m_timeState = timeState;
	}

	 virtual void toEatMeal(EatMeal *pEatMeal);
	
private:
	int m_timeState;
};

// 具體狀態類(ConcreteState)- 吃晚飯
class NightTimeState : public TimeState
{
public:
	void changeTimeState(int timeState)
	{
		m_timeState = timeState;
	}

	virtual void toEatMeal(EatMeal *pEatMeal);
private:
	int m_timeState;
};

/*因爲MorningTimeState、NoonTimeState、NightTimeState三個類中互相調用了類中的方法,
即使前置聲明這三個類也不行,類的聲明和實現必須分離才行,不分離的話會提示“使用了未定義的類型錯誤”*/

void MorningTimeState::toEatMeal(EatMeal *pEatMeal)
{
	if (pEatMeal->getCurTime() >= 7 && pEatMeal->getCurTime() <= 8) // 早飯
	{
		cout << "ISMILELI------------>洗刷完了,我要去吃早飯啦!" << endl;
	}
	else if(pEatMeal->getCurTime() >= 12 && pEatMeal->getCurTime() <= 13) // 下一個吃飯節點午飯
	{
		MorningTimeState *pMorningTimeState = dynamic_cast<MorningTimeState*>(pEatMeal->getTimeState());
		DELETE_PTR(pMorningTimeState);
		TimeState *pNoonTimeState = dynamic_cast<TimeState*>(new NoonTimeState);
		pEatMeal->setTimeState(pNoonTimeState);
		pEatMeal->getTimeState()->toEatMeal(pEatMeal);
	}
	else // 其他時間都在工作
	{
		cout << "ISMILELI------------>我是工作狂,我不愛睡覺,除了吃飯就是工作!" << endl;
	}
}

void NoonTimeState::toEatMeal(EatMeal *pEatMeal)
{
	if (pEatMeal->getCurTime() >= 12 && pEatMeal->getCurTime() <= 13) // 午飯
	{
		cout << "ISMILELI------------>中午到了,我要去吃午飯啦!" << endl;
	}
	else if(pEatMeal->getCurTime() >= 18 && pEatMeal->getCurTime() <= 20)// 下一個吃飯節點晚飯
	{
		NoonTimeState *pNoonTimeState = dynamic_cast<NoonTimeState*>(pEatMeal->getTimeState());
		DELETE_PTR(pNoonTimeState);
		TimeState *pNightTimeState = dynamic_cast<TimeState*>(new NightTimeState);
		pEatMeal->setTimeState(pNightTimeState);
		pEatMeal->getTimeState()->toEatMeal(pEatMeal);
	}
	else // 其他時間都在工作
	{
		cout << "ISMILELI------------>我是工作狂,我不愛睡覺,除了吃飯就是工作!" << endl;
	}
}

void NightTimeState::toEatMeal(EatMeal *pEatMeal)
{
	if (pEatMeal->getCurTime() >= 18 && pEatMeal->getCurTime() <= 20) // 晚飯
	{
		cout << "ISMILELI------------>下班時間到了,我要去吃晚飯啦!" << endl;
	}
	else if(pEatMeal->getCurTime() >= 7 && pEatMeal->getCurTime() <= 8)// 下一個吃飯節點早飯
	{
		NightTimeState *pNightTimeState = dynamic_cast<NightTimeState*>(pEatMeal->getTimeState());
		DELETE_PTR(pNightTimeState);
		TimeState *pMorningTimeState = dynamic_cast<TimeState*>(new MorningTimeState);
		pEatMeal->setTimeState(pMorningTimeState);
		pEatMeal->getTimeState()->toEatMeal(pEatMeal);
	}
	else // 其他時間都在工作
	{
		cout << "ISMILELI------------>我是工作狂,我不愛睡覺,除了吃飯就是工作!" << endl;
	}
}

int main()
{
	cout << "----------------------狀態模式----------------------" << endl;
	EatMeal *pEatMeal = new EatMeal;
	TimeState *pCurTimeState = dynamic_cast<TimeState*>(new MorningTimeState);
	pEatMeal->setTimeState(pCurTimeState);
	
	pEatMeal->changeTimeState(7);
	pEatMeal->toEatMeal();

	pEatMeal->changeTimeState(13);
	pEatMeal->toEatMeal();

	
	pEatMeal->changeTimeState(18);
	pEatMeal->toEatMeal();

	pEatMeal->changeTimeState(21);
	pEatMeal->toEatMeal();

	// 指針pCurTimeState內存釋放在pEatMeal析構函數中
	DELETE_PTR(pEatMeal);

    std::cout << "Hello World!\n";
	getchar();
}

運行結果:
在這裏插入圖片描述

五、狀態模式的優缺點

優點:
1、把複雜的對象狀態判斷簡單化。
2、將所有與某個狀態有關的行爲放到一個類中,可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行爲。
3、允許狀態轉換邏輯與狀態對象合成一體,從而簡化邏輯判斷。
4、多個環境對象可以共享一個狀態對象,減少系統中對象的個數。

缺點:
1、每增加一種狀態都會增加一個類。
2、狀態模式的結構與實現都較爲複雜。
3、耦合性強,增加新的狀態類需要修改負責狀態轉換的源代碼。

能力有限,如有錯誤,多多指教。。。

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