基于C++的《Head First设计模式》笔记——状态模式

目录

一.专栏简介

二.状态图

三.State接口和类

四.状态模式和定义

五.策略模式和状态模式的区别

六.完整代码和运行结果

七.总结


一.专栏简介

本专栏是我学习《head first》设计模式的笔记。这本书中是用Java语言为基础的,我将用C++语言重写一遍,并且详细讲述其中的设计模式,涉及是什么,为什么,怎么做,自己的心得等等。希望阅读者在读完我的这个专题后,也能在开发中灵活且正确的使用,或者在面对面试官时,能够自信地说自己熟悉常用设计模式。

本章将开始状态模式的学习,这是策略模式的双胞胎兄弟。

二.状态图

这是一个状态图,每个圆圈是一个状态,总共五个状态,箭头上的文字是行为,至少五种行为。

我们打算把状态对象封装进它们的类,让后在动作发生时让机器类委托给当前状态。在这里,我们遵循以下设计原则,因此最后应该得到一个更容易维护的设计。我们打算这么做:

  1. 首先,我们打算定义一个State接口,里面针对糖果机内的每个动作有一个方法。
  2. 然后我们打算为机器的每个状态实现一个State类。这些类将负责在相应状态下的机器行为。
  3. 最后,我们打算去除所有条件代码,把工作委托给State类。

我们不仅遵循设计原则,正如我们将看到的,实际上我们实现了状态模式。不过,在重新编写我们代码后,我们再学习官方的状态模式......

三.State接口和类

首先我们来为State创建一个接口,所有状态都要实现它:

把设计中的每个状态封装进一个实现State接口的类。

是时候实现状态了,我们知道了我们要什么行为,只需要把它变成代码。代码如下:

state.h:

cpp 复制代码
#pragma once

#include <iostream>
using namespace std;

struct State
{
	virtual void insertQuarter() = 0;
	virtual void ejectQuarter() = 0;
	virtual void turnCrank() = 0;
	virtual void dispense() = 0;
};

class NoQuarterState : public State
{
public:
	NoQuarterState(GumballMachine* gumballMachine):
		_gumballMachine(gumballMachine)
	{}
	void insertQuarter() override
	{
		cout << "你放入了25美分" << endl;
		_gumballMachine->setState(_gumballMachine->getHasQuarterState());
	}
	void ejectQuarter() override
	{
		cerr << "你尚未投入25分钱" << endl;
	}
	void turnCrank() override
	{
		cerr << "你转动曲柄但没有25分钱" << endl;
	}
	void dispense() override
	{
		cerr << "你需要先投钱" << endl;
	}
private:
	GumballMachine* _gumballMachine;
};

class HasQuarterState : public State
{
public:
	HasQuarterState(GumballMachine* gumballMachine):
		_gumballMachine(gumballMachine)
	{}
	void insertQuarter() override
	{
		cerr << "你不能投入另一个25分钱" << endl;
	}
	void ejectQuarter() override
	{
		cout << "已退回25分钱" << endl;
		_gumballMachine->setState(_gumballMachine->getNoQuarterState());
	}
	void turnCrank() override
	{
		cout << "已转动" << endl;
		_gumballMachine->setState(_gumballMachine->getSoldState());
	}
	void dispense() override
	{
		cerr << "没发放糖果" << endl;
	}
private:
	GumballMachine* _gumballMachine;
};

class SoldState : public State
{
public:
	SoldState(GumballMachine* gumballMachine):
		_gumballMachine(gumballMachine)
	{}
	void insertQuarter() override
	{
		cerr << "请稍后,我们马上给你糖果" << endl;
	}
	void ejectQuarter() override
	{
		cerr << "抱歉,你已经转过曲柄" << endl;
		_gumballMachine->setState(_gumballMachine->getNoQuarterState());
	}
	void turnCrank() override
	{
		cerr << "不会因为转两次就再给你一颗糖果" << endl;
	}
	void dispense() override
	{
		cerr << "发放糖果" << endl;
		_gumballMachine->releaseBall();
		int count = _gumballMachine->getCount();
		if(count) _gumballMachine->setState(_gumballMachine->getNoQuarterState());
		else
		{
			cerr << "没糖果了" << endl;
			_gumballMachine->setState(_gumballMachine->getSoldOutState());
		}
	}
private:
	GumballMachine* _gumballMachine;
};

class SoldOutState : public State
{
public:
	SoldOutState(GumballMachine* gumballMachine) :
		_gumballMachine(gumballMachine)
	{}
	void insertQuarter() override
	{
		cerr << "机器已经售罄" << endl;
	}
	void ejectQuarter() override
	{
		cerr << "你还没有投入25分钱" << endl;
	}
	void turnCrank() override
	{
		cerr << "机器已经售罄" << endl;
	}
	void dispense() override
	{
		cerr << "机器已经售罄" << endl;
	}
private:
	GumballMachine* _gumballMachine;
};

class WinnerState : public State
{
public:
	WinnerState(GumballMachine* gumballMachine):
		_gumballMachine(gumballMachine)
	{}
	void insertQuarter() override
	{
		cerr << "请稍后,我们马上给你糖果" << endl;
	}
	void ejectQuarter() override
	{
		cerr << "抱歉,你已经转过曲柄" << endl;
	}
	void turnCrank() override
	{
		cerr << "不会因为转两次就再给你一颗糖果" << endl;
	}
	void dispense() override
	{
		_gumballMachine->releaseBall();
		int count1 = _gumballMachine->getCount();
		if (count1 > 0)
		{
			_gumballMachine->releaseBall();
			int count2 = _gumballMachine->getCount();
			if (count2) _gumballMachine->setState(_gumballMachine->getNoQuarterState());
			else _gumballMachine->setState(_gumballMachine->getSoldOutState());
		}
	}
private:
	GumballMachine* _gumballMachine;
};

class GumballMachine
{
public:
	GumballMachine(int numberGumballs) :
		soldOutState(new SoldOutState(this)),
		noQuarterState(new NoQuarterState(this)),
		hasQuarterState(new HasQuarterState(this)),
		soldState(new SoldState(this)),
		count(numberGumballs)
	{
		if (count) state = noQuarterState;
		else state = soldOutState;
	}
	void insertQuarter() { state->insertQuarter(); }
	void ejectQuarter() { state->ejectQuarter(); }
	void turnCrank()
	{
		state->turnCrank();
		state->dispense();
	}
	void setState(State* s) { state = s; }
	void releaseBall()
	{
		cout << "放出一颗糖果" << endl;
		if (count > 0) --count;
	}
	int getCount() { return count; }
	State* getSoldOutState() { return soldOutState; }
	State* getNoQuarterState() { return noQuarterState; }
	State* getHasQuarterState() { return hasQuarterState; }
	State* getSoldState() { return soldState; }
private:
	State* soldOutState;
	State* noQuarterState;
	State* hasQuarterState;
	State* soldState;

	int count = 0;
	State* state;
};
  1. 把每个状态的行为局部化到它自己的类中。
  2. 避免所有烦人,难以维护的if语句。
  3. 通过添加新的状态类(我们马上就这样做),使得每一个状态对修改关闭,糖果机对扩展开放。
  4. 创建更紧密映射万能糖果公司状态图、更容易阅读和理解的代码以及类结构。

现在继续来看看我们已经做了的功能方面:

文字描述如图中所示:

四.状态模式和定义

状态模式允许对象在内部状态改变时改变其行为。对象看起来好像改变了它的类。

实际上我们知道我们是在使用组合来简单引用不同的状态对象,造成类改变的假象。

状态模式类图:

五.策略模式和状态模式的区别

对于状态模式,我们把一组行为封装进状态对象,context随时(把责任)委托给这些状态之一。随着时间的推移,当前状态不断在各个状态对象之间改变,以反映context的内部状态,因此,context的行为也随着时间的推移而改变。客户通常对状态对象知道得很少,甚至不知道。

对于策略,客户通常指定context要组合的策略对象。现在,虽然模式提供了弹性,可以在运行时改变策略对象,但对于一个context对象来说,经常有一个最适合的策略对象。

一般来说,把策略模式想成子类化的一个弹性替代方案;如果你使用继承定义了一个类的行为,那么你就和这个行为黏在一起了,很难改变。有了策略,我们可以通过组合不同的对象改变行为。

把状态模式想成在context中放进许多条件的一个替代方案,通过把行为封装进状态对象,你可以简单地在context中改变状态对象来改变其行为。

六.完整代码和运行结果

完整代码中我们加入了赢家状态。

state.h:

cpp 复制代码
#pragma once

#include <iostream>
#include <ctime>
using namespace std;

struct State
{
	virtual void insertQuarter() = 0;
	virtual void ejectQuarter() = 0;
	virtual void turnCrank() = 0;
	virtual void dispense() = 0;
	virtual void refill() = 0;
};

class GumballMachine
{
public:
	GumballMachine(int numberGumballs);
	void insertQuarter() { state->insertQuarter(); }
	void ejectQuarter() { state->ejectQuarter(); }
	void turnCrank()
	{
		state->turnCrank();
		state->dispense();
	}
	void setState(State* s) { state = s; }
	void releaseBall()
	{
		cout << "放出一颗糖果" << endl;
		if (count > 0) --count;
	}
	void refill(int addCount)
	{
		count += addCount;
		cout << "加入了" << addCount << "颗糖果,现在糖果总数为" << count << "颗" << endl;
		state->refill();
	}
	int getCount() const { return count; }
	State* getSoldOutState() { return soldOutState; }
	State* getNoQuarterState() { return noQuarterState; }
	State* getHasQuarterState() { return hasQuarterState; }
	State* getSoldState() { return soldState; }
	State* getWinnerState() { return winnerState; }
private:
	State* soldOutState;
	State* noQuarterState;
	State* hasQuarterState;
	State* soldState;
	State* winnerState;

	int count = 0;
	State* state;
};

class NoQuarterState : public State
{
public:
	NoQuarterState(GumballMachine* gumballMachine):
		_gumballMachine(gumballMachine)
	{}
	void insertQuarter() override
	{
		cout << "你放入了25美分" << endl;
		_gumballMachine->setState(_gumballMachine->getHasQuarterState());
	}
	void ejectQuarter() override
	{
		cerr << "你尚未投入25分钱" << endl;
	}
	void turnCrank() override
	{
		cerr << "你转动曲柄但没有25分钱" << endl;
	}
	void dispense() override
	{
		cerr << "你需要先投钱" << endl;
	}
	void refill() override
	{

	}
private:
	GumballMachine* _gumballMachine;
};

class HasQuarterState : public State
{
public:
	HasQuarterState(GumballMachine* gumballMachine):
		_gumballMachine(gumballMachine)
	{
		srand((unsigned int)time(NULL));
	}
	void insertQuarter() override
	{
		cerr << "你不能投入另一个25分钱" << endl;
	}
	void ejectQuarter() override
	{
		cout << "已退回25分钱" << endl;
		_gumballMachine->setState(_gumballMachine->getNoQuarterState());
	}
	void turnCrank() override
	{
		cout << "已转动" << endl;
		int r = rand() % 10;
		if(r == 0 && _gumballMachine->getCount() > 1) _gumballMachine->setState(_gumballMachine->getWinnerState());
		else _gumballMachine->setState(_gumballMachine->getSoldState());
	}
	void dispense() override
	{
		cerr << "没发放糖果" << endl;
	}
	void refill() override
	{

	}
private:
	GumballMachine* _gumballMachine;
};

class SoldState : public State
{
public:
	SoldState(GumballMachine* gumballMachine):
		_gumballMachine(gumballMachine)
	{}
	void insertQuarter() override
	{
		cerr << "请稍后,我们马上给你糖果" << endl;
	}
	void ejectQuarter() override
	{
		cerr << "抱歉,你已经转过曲柄" << endl;
		_gumballMachine->setState(_gumballMachine->getNoQuarterState());
	}
	void turnCrank() override
	{
		cerr << "不会因为转两次就再给你一颗糖果" << endl;
	}
	void dispense() override
	{
		cout << "发放糖果" << endl;
		_gumballMachine->releaseBall();
		int count = _gumballMachine->getCount();
		if(count) _gumballMachine->setState(_gumballMachine->getNoQuarterState());
		else
		{
			cout << "没糖果了" << endl;
			_gumballMachine->setState(_gumballMachine->getSoldOutState());
		}
	}
	void refill() override
	{

	}
private:
	GumballMachine* _gumballMachine;
};

class SoldOutState : public State
{
public:
	SoldOutState(GumballMachine* gumballMachine) :
		_gumballMachine(gumballMachine)
	{}
	void insertQuarter() override
	{
		cerr << "机器已经售罄" << endl;
	}
	void ejectQuarter() override
	{
		cerr << "你还没有投入25分钱" << endl;
	}
	void turnCrank() override
	{
		cerr << "机器已经售罄" << endl;
	}
	void dispense() override
	{
		cerr << "机器已经售罄" << endl;
	}
	void refill() override
	{
		_gumballMachine->setState(_gumballMachine->getNoQuarterState());
	}
private:
	GumballMachine* _gumballMachine;
};

class WinnerState : public State
{
public:
	WinnerState(GumballMachine* gumballMachine):
		_gumballMachine(gumballMachine)
	{}
	void insertQuarter() override
	{
		cerr << "请稍后,我们马上给你糖果" << endl;
	}
	void ejectQuarter() override
	{
		cerr << "抱歉,你已经转过曲柄" << endl;
	}
	void turnCrank() override
	{
		cerr << "不会因为转两次就再给你一颗糖果" << endl;
	}
	void dispense() override
	{
		_gumballMachine->releaseBall();
		int count1 = _gumballMachine->getCount();
		if (count1 > 0)
		{
			_gumballMachine->releaseBall();
			cout << "你是赢家,你获得了两颗糖果" << endl;
			int count2 = _gumballMachine->getCount();
			if (count2) _gumballMachine->setState(_gumballMachine->getNoQuarterState());
			else _gumballMachine->setState(_gumballMachine->getSoldOutState());
		}
		else
		{
			cout << "没糖果了" << endl;
			_gumballMachine->setState(_gumballMachine->getSoldOutState());
		}
	}
	void refill() override
	{

	}
private:
	GumballMachine* _gumballMachine;
};

GumballMachine::GumballMachine(int numberGumballs) :
	soldOutState(new SoldOutState(this)),
	noQuarterState(new NoQuarterState(this)),
	hasQuarterState(new HasQuarterState(this)),
	soldState(new SoldState(this)),
	winnerState(new WinnerState(this)),
	count(numberGumballs)
{
	if (count) state = noQuarterState;
	else state = soldOutState;
}

在代码中我们加入了赢家状态,并且在有25分钱状态里我们在转动把柄后用随机数判断是不是赢家,进而跳转到不同的状态。另外还新增了refill方法,支持往机器中加入更多糖果。

main.cpp:

cpp 复制代码
#include "state.h"

int main()
{
	GumballMachine gumballMachine(5);

	gumballMachine.insertQuarter();
	gumballMachine.turnCrank();
	gumballMachine.insertQuarter();
	gumballMachine.turnCrank();
	gumballMachine.insertQuarter();
	gumballMachine.turnCrank();
	gumballMachine.insertQuarter();
	gumballMachine.turnCrank();
	gumballMachine.insertQuarter();
	gumballMachine.turnCrank();

	gumballMachine.refill(10);

	gumballMachine.insertQuarter();
	gumballMachine.turnCrank();
	gumballMachine.insertQuarter();
	gumballMachine.turnCrank();

	return 0;
}

运行结果:

七.总结

  • 状态模式允许一个对象基于内部状态拥有许多不同的行为。
  • 和过程式状态机不同,状态模式用真正的类代表每个状态。
  • Context把行为委托给所组合的当前状态对象。
  • 通过把每个状态封装进一个类,我们把以后需要做的任何改变局部化了。
  • 状态和策略模式有相同的类图,但它们的意图不同。
  • 策略模式通常会用行为或算法来配置Context类。
  • 状态模式允许Context随着状态的改变来改变行为。
  • 状态迁移可以由State类或Context类来控制。
  • 使用状态模式通常会导致设计中类数目增加。
  • 状态类可以在多个Context实例之间共享。
相关推荐
阳明Coding2 小时前
golang从入门到通天—数据库操作(gorm框架使用)(最简单最详细的golang学习笔记)
笔记·学习·golang
顶点多余2 小时前
静态链接 vs 动态链接,静态库 vs 动态库
linux·c++·算法
AI视觉网奇2 小时前
ue5 开发 web socket server 实战2026
c++·学习·ue5
Kapibalapikapi2 小时前
思考笔记 | 为什么需要“获取CDN后面的真实IP”
笔记·web安全·思考记录
鄭郑2 小时前
【Playwright学习笔记 02】CSS-selector定位
笔记·学习
王老师青少年编程2 小时前
2024年3月GESP真题及题解(C++八级): 接竹竿
c++·题解·真题·gesp·csp·八级·接竹竿
2501_937798392 小时前
2026 AI搜索优化监测工具白皮书:免费版VS付费版效能拆解
笔记
偷星星的贼112 小时前
C++中的访问者模式实战
开发语言·c++·算法
Engineer邓祥浩3 小时前
设计模式学习(18) 23-16 迭代器模式
学习·设计模式·迭代器模式