状态 模式

State的定义:不同的状态,不同的行为;或者说,每个状态有着相应的行为。

一、何时使用状态模式

State模式在实际使用中比较多,适合"状态的切换"。因为我们经常会使用If elseif else 进行状态切换, 如果针对状态的这样判断切换反复出现,我们就要联想到是否可以采取State模式了。

不只是根据状态,也有根据属性。如果某个对象的属性不同,对象的行为就不一样,这点在数据库系统中出现频率比较高,我们经常会在一个数据表的尾部,加上property属性含义的字段,用以标识记录中一些特殊性质的记录,这种属性的改变(切换)又是随时可能发生的,就有可能要使用State。

在实际使用,类似开关一样的状态切换是很多的,但有时并不是那么明显,取决于你的经验和对系统的理解深度。

这里要阐述的是"开关切换状态" 和" 一般的状态判断"是有一些区别的," 一般的状态判断"也是有 if..elseif结构,例如:

if (which==1) state="hello";
else if (which==2) state="hi";
else if (which==3) state="bye";
这是一个 " 一般的状态判断",state值的不同是根据which变量来决定的,which和state没有关系。如果改成:
if (state.euqals("bye")) state="hello";
else if (state.euqals("hello")) state="hi";
else if (state.euqals("hi")) state="bye";

这就是 "开关切换状态",是将state的状态从"hello"切换到"hi",再切换到""bye";在切换到"hello",好象一个旋转开关,这种状态改变就可以使用State模式了。

如果单纯有上面一种将"hello"–>"hi"–>"bye"–>"hello"这一个方向切换,也不一定需要使用State模式,因为State模式会建立很多子类,复杂化,但是如果又发生另外一个行为:将上面的切换方向反过来切换,或者需要任意切换,就需要State了。

请看下例:

class Context{

private:
    Color state=null;

public:
    void push(){
    //如果当前red状态 就切换到blue
    if (state==Color.red) state=Color.blue;
    //如果当前blue状态 就切换到green
    else if (state==Color.blue) state=Color.green;
    //如果当前black状态 就切换到red
    else if (state==Color.black) state=Color.red;
    //如果当前green状态 就切换到black
    else if (state==Color.green) state=Color.black;
    
        Sample sample=new Sample(state);
    sample.operate();
  }

    void pull(){
    //与push状态切换正好相反
    if (state==Color.green) state=Color.blue;
    else if (state==Color.black) state=Color.green;
    else if (state==Color.blue) state=Color.red;
    else if (state==Color.red) state=Color.black;
    
        Sample2 sample2=new Sample2(state);
    sample2.operate();
  }
}

在上例中,我们有两个动作push推和pull拉,这两个开关动作,改变了Context颜色,至此,我们就需要使用State模式优化它。

另外注意:但就上例,state的变化,只是简单的颜色赋值,这个具体行为是很简单的,State适合巨大的具体行为,因此在,就本例,实际使用中也不一定非要使用State模式,这会增加子类的数目,简单的变复杂。

二、状态模式是什么?

     状态模式是一种行为型的软件设计模式,当一个对象的内在状态改变时,其行为也随之改变。就像玩游戏的时候,不同的buff状态,角色会有不同的伤害、技能等等。

       当控制一个对象状态的条件表达式过于复杂时,很适合用该模式,将复杂的判断逻辑转移到表示不同状态的系列类中,能将逻辑大大简化。

       状态模式的优点:

良好封装性。每个状态的行为被封装到对应类中。
便于维护。减少了if else或switch语句的出现,适用于条件判断复杂的场景。
良好扩展性。添加状态更便捷。
      状态模式的缺点:

状态数量增加,类数量也会增加,对开发者要求较高。
状态少时,应用状态模式会显得冗余。

三、

状态模式主要包含以下角色:

1. Context(上下文):定义客户感兴趣的接口,并且维护一个ConcreteState子类(具体状态)的实例(如:FreeState),这个实例定义当前状态。
2. State(状态):定义一个接口,以封装与Context的一个特定状态 相关的行为(如:enterTime\calculateFee)。
3. ConcreteState(具体状态):每一个具体状态类实现了State接口。

停车收费系统

-- 实现一个停车收费系统,根据停车时间计算费用,不同的时间段有不同的收费标准

ParkingMeter类构造函数=》FreeState类型对象=》ParkingMeter、PaidState

#include <iostream>
#include <memory>

// 状态接口类=》封装和状态相关的行为
class ParkingState {
public:
    virtual void enterTime(ParkingMeter* meter, int minutes) = 0;
    virtual void calculateFee(ParkingMeter* meter) = 0;
};

// 具体状态类:免费时间段
class FreeState : public ParkingState {
public:
	// FreeState方法的实现
	void FreeState::enterTime(ParkingMeter* meter, int minutes) 
	{
		int newTotalMinutes = meter->getTotalMinutes() + minutes;
		meter->setFee(0.0); // 免费时间段,费用为0
	 
		if (newTotalMinutes > 60) 
		{
			// 超过60分钟,进入收费时间段
			meter->setState(std::make_unique<PaidState>());
			meter->addTime(newTotalMinutes - 60);
		} else {
			// 在免费时间段内
			std::cout << "Time entered: " << minutes << " minutes. Total time: " << newTotalMinutes << " minutes." << std::endl;
		}
	}
	 
	void FreeState::calculateFee(ParkingMeter* meter) 
	{
		std::cout << "Total parking fee: $" << meter->getFee() << " (Free period)" << std::endl;
	}
};
 
// 具体状态类:收费时间段
class PaidState : public ParkingState {
public:
	// PaidState方法的实现
	void PaidState::enterTime(ParkingMeter* meter, int minutes) 
	{
		int newTotalMinutes = meter->getTotalMinutes() + minutes;
		meter->setFee(newTotalMinutes * 0.5); // 收费时间段,每分钟0.5美元
	 
		std::cout << "Time entered: " << minutes << " minutes. Total time: " << newTotalMinutes << " minutes." << std::endl;
	}
	 
	void PaidState::calculateFee(ParkingMeter* meter) 
	{
		std::cout << "Total parking fee: $" << meter->getFee() << " (Paid period)" << std::endl;
	}
};


// 上下文类
class ParkingMeter {
private:
    std::unique_ptr<ParkingState> state;
    int totalMinutes;
    double fee;
 
public:	
	// ParkingMeter方法的实现
	ParkingMeter::ParkingMeter() : totalMinutes(0), fee(0.0) 
	{
		state = std::make_unique<FreeState>();
	}
	 
	void ParkingMeter::setState(std::unique_ptr<ParkingState> newState) 
	{
		state = std::move(newState);
	}
	 
	void ParkingMeter::addTime(int minutes) 
	{
		state->enterTime(this, minutes);
	}
	 
	void ParkingMeter::calculateFee() 
	{
		state->calculateFee(this);
	}
	 
	int ParkingMeter::getTotalMinutes() const 
	{
		return totalMinutes;
	}
	 
	void ParkingMeter::setFee(double newFee) 
	{
		fee = newFee;
	}
	 
	double ParkingMeter::getFee() const 
	{
		return fee;
	}
};
// 客户端代码
int main()
{
    ParkingMeter meter;
    meter.addTime(30); // 免费时间段
    meter.calculateFee();
 
    meter.addTime(40); // 转入收费时间段
    meter.calculateFee();
 
    meter.addTime(20); // 收费时间段
    meter.calculateFee();
 
    return 0;
}

原文链接:C++中的状态模式_c++ 状态模式-CSDN博客

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值