Head First设计模式C++实现--第十章:状态(State)模式

本文介绍了如何使用设计模式中的状态模式来改进糖果机代码,使其更易于扩展。通过创建State接口和具体状态类,将不同状态的行为封装在各自类中,以避免在核心糖果机类中使用复杂的条件逻辑。文章详细阐述了设计过程,包括问题的提出、设计思路以及具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

状态(State)模式

一、问题的提出

糖果机的CEO正在使用一款软件,现在需要让糖果机控制器如下图般工作:

二、一般设计思路

1、找出所有状态

没有25分钱、有25分钱、糖果售罄、售出糖果

2、创建实例变量来持有目前的状态,定义每个状态的值

#define SOLD_OUT 0//糖果售罄
#define NO_QUARTER 1//没有25分钱
#define HAS_QUARTER 2//有25分钱
#define SOLD 3//售出糖果

3、创建状态机类

现在,我们创建一个类,它的作用对象就是一个状态机。对每一个动作,我们都创建了一个对应的方法,这些方法利用条件语句来决定在每个状态内什么行为是恰当的。比如对“投入25分钱”这个动作来说,我们可以把对应的方法写成下面的养子:


//投入硬币动作
void insertQuarter()
{
	if(HAS_QUARTER == state)
	{
		//当前状态是“有硬币”
		cout<<"You can't insert another quarter"<<endl;
	}
	else if(SOLD_OUT == state)
	{
		//当前状态是“糖果售罄”
		cout<<"You can't insert a quarter, the machine is sold out"<<endl;
	}
	else if(SOLD == state)
	{
		//当前状态是“售出糖果”
		cout<<"Please wait,we're already giving you a gumball"<<endl;
	}
	else if(NO_QUARTER == state)
	{
		//当前状态是“没有硬币”
		state = HAS_QUARTER;
		cout<<"You inserted a quarter"<<endl;
	}
}

4、实现代码

#define SOLD_OUT	0
#define NO_QUARTER	1
#define HAS_QUARTER	2
#define SOLD		3
//糖果机类
class GumballMachine
{
public:
	GumballMachine(int count);//构造函数,传入糖果数量
	void insertQuarter();//顾客投入25分硬币状态
	void ejectQuarter();//顾客要求退回硬币状态
	void turnCrank();//转动曲柄状态
	void dispense();//发放糖果状态
};
以上就是糖果机的类。实现类中的函数就行了。


5、变更请求

现在需要将“购买糖果”变成一个游戏:当曲柄被转动时,有10%的几率掉下来的是两颗糖果。

由以上考虑周详的方法写糖果机代码,但是不容易扩展,因为在投币、退币、转动曲柄、发放糖果等状态需要添加代码。turnCrack()尤其会变得很乱,因为必须加上代码来检查目前的顾客是否是赢家,然后再决定是切换到赢家状态还是售出糖果状态。

因此,以上的糖果类不能再继续使用,但是应该怎么变化呢??封装变化。

三、新的设计

3.1设计方式

新的设计计划是这样的:不要维护现有代码,重写它以便将状态对象封装在各自的类中,然后在动作发生时委托给当前状态。

因此,需要做的事情是:

1、定义一个State接口。在这个接口内,糖果机的每个动作都有一个对应的方法。

2、为机器中的每个状态实现状态类。这些类将负责在对应的状态下进行机器的行为。

3、最后,我们要摆脱旧的条件代码,取而代之的方式是,将动作委托到状态类。


定义状态接口和类:


将所有的状态直接映射到类。


3.2实现状态类和糖果机类

头文件:


#ifndef STATE__H
#define STATE__H
#include <iostream>
using namespace std;

//状态超类
class State
{
public:
	virtual void insertQuarter() = 0;//顾客投入25分硬币状态
	virtual void ejectQuarter() = 0;//顾客要求退回硬币状态
	virtual void turnCrank() = 0;//转动曲柄状态
	virtual void dispense() = 0;//发放糖果状态
};
//没有25分钱状态
class NoQuarterState : public State
{
private:
	GumballMachine* gumbalMachine;
public:
	NoQuarterState(GumballMachine* gumbalMachine);

	void insertQuarter();//顾客投入25分硬币状态
	void ejectQuarter();//顾客要求退回硬币状态
	void turnCrank();//转动曲柄状态
	void dispense();//发放糖果状态
};
//售出状态
class SoldState : public State
{
private:
	GumballMachine* gumbalMachine;
public:
	SoldState(GumballMachine* gumbalMachine);

	void insertQuarter();//顾客投入25分硬币状态
	void ejectQuarter();//顾客要求退回硬币状态
	void turnCrank();//转动曲柄状态
	void dispense();//发放糖果状态
};
//有25分钱状态
class HasQuarterState : public State
{
private:
	GumballMachine* gumbalMachine;
public:
	HasQuarterState(GumballMachine* gumbalMachine);

	void insertQuarter();//顾客投入25分硬币状态
	void ejectQuarter();//顾客要求退回硬币状态
	void turnCrank();//转动曲柄状态
	void dispense();//发放糖果状态
};
//售罄状态
class SoldOutState : public State
{
private:
	GumballMachine* gumbalMachine;
public:
	SoldOutState(GumballMachine* gumbalMachine);

	void insertQuarter();//顾客投入25分硬币状态
	void ejectQuarter();//顾客要求退回硬币状态
	void turnCrank();//转动曲柄状态
	void dispense();//发放糖果状态
};

//糖果机类
class GumballMachine
{
private:
	State* soldOutState;//糖果售罄
	State* noQuarterState;//没有25分钱
	State* hasQuarterState;//有25分钱
	State* soldState;//售出糖果

	State* state;//糖果机当前状态
	int count;	//糖果数目
public:
	GumballMachine(int numberGumballs);

	void insertQuarter();
	void ejectQuarter();
	void turnCrank();
	void dispense();

	//各个状态的get方法
	State* getNoQuarterState();
	State* getSoldOutState();
	State* getSoldState();
	State* getHasQuarterState();

	void SetState(State* state);
	//获取糖果数目
	int getCount();
	void releaseBall();
};

#endif

#include "GumballMachine.h"
//NoQuarterState状态类实现
NoQuarterState::NoQuarterState(GumballMachine* gumbalMachine)//通过构造函数得到糖果机引用,然后将它记录在实例变量
{
	this->gumbalMachine = gumbalMachine;
}
void NoQuarterState::insertQuarter()
{
	cout<<"You inserted a quarter"<<endl;
	this->gumbalMachine->SetState(gumbalMachine->getHasQuarterState());
}
void NoQuarterState::ejectQuarter()
{
	cout<<"You haven't insert a quarter"<<endl;
}
void NoQuarterState::turnCrank()
{
	cout<<"You turned, but there's no quarter"<<endl;
}
void NoQuarterState::dispense()
{
	cout<<"You need to pay first"<<endl;
}

//HasQuarterState状态类实现
HasQuarterState::HasQuarterState(GumballMachine* gumbalMachine)
{
	this->gumbalMachine = gumbalMachine;
}
void HasQuarterState::insertQuarter()
{
	cout<<"You can't insert another quarter"<<endl;
}
void HasQuarterState::ejectQuarter()
{
	cout<<"Quarter returned"<<endl;
	this->gumbalMachine->SetState(gumbalMachine->getNoQuarterState());
}
void HasQuarterState::turnCrank()
{
	cout<<"You turned..."<<endl;
	this->gumbalMachine->SetState(this->gumbalMachine->getSoldState());
}
void HasQuarterState::dispense()
{
	cout<<"No gumball dispensed"<<endl;
}

//SoldState状态类实现
SoldState::SoldState(GumballMachine* gumbalMachine)
{
	this->gumbalMachine = gumbalMachine;
}
void SoldState::insertQuarter()
{
	cout<<"Please wait, we're already giving you a gumball"<<endl;
}
void SoldState::ejectQuarter()
{
	cout<<"Sorry, you already turned the crank"<<endl;
}
void SoldState::turnCrank()
{
	cout<<"Turning twice doesn't get you another gumball"<<endl;
}
void SoldState::dispense()
{
	this->gumbalMachine->releaseBall();
	if(gumbalMachine->getCount() > 0)
		gumbalMachine->SetState(gumbalMachine->getNoQuarterState());
	else
	{
		cout<<"Oops , out of gumballs!"<<endl;
		gumbalMachine->SetState(gumbalMachine->getSoldOutState());
	}
}

GumballMachine::GumballMachine(int numberGumballs)//糖果机构造函数
{
	soldOutState = new SoldOutState(this);
	noQuarterState = new NoQuarterState(this);
	hasQuarterState = new HasQuarterState(this);
	soldOutState = new SoldOutState(this);
	this->count = numberGumballs;
	if(numberGumballs > 0 )//超过0颗糖果,状态设为NoQuarterState
		state = noQuarterState;
}
void GumballMachine::insertQuarter()
{
	state->insertQuarter();
}
void GumballMachine::ejectQuarter()
{
	state->ejectQuarter();
}
void GumballMachine::turnCrank()
{
	state->turnCrank();
	state->dispense();//注意,我们不需要在糖果机种准备一个dispense动作的方法,因为这只是一个内部动作;用户可以不直接要求机器发送糖果。但是我们是在状态对象的turnCrank方法中调用dispense方法的
}
void GumballMachine::SetState(State* state)
{
	this->state = state;
}
//获取每个状态的方法
State* GumballMachine::getNoQuarterState()
{
	return noQuarterState;
}
State* GumballMachine::getHasQuarterState()
{
	return hasQuarterState;
}
State* GumballMachine::getSoldOutState()
{
	return soldOutState;
}
State* GumballMachine::getSoldState()
{
	return soldState;
}
int GumballMachine::getCount()
{
	return count;
}
void GumballMachine::releaseBall()
{
	cout<<"A gumball comes rolling out the slot..."<<endl;
	if(count != 0)
		count--;
}

四、定义状态模式


状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类
这个描述中的第一部分附有相当多的含义,因为这个模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象,我们知道行为会随着内部状态改变而改变。而这个定义中的第二部分“看起来好像修改了它的类”是什么意思呢?从客户的视角来看:如果说你使用的对象能够完全改变它的行为,那你会觉得这个对象实际上是从别的类实例化而来的。然而,实际上是在使用组合通过简单引用不同的状态来造成类改变的假象。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值