概述
作为一种行为设计模式,状态模式允许对象在其内部状态改变时,改变其行为。这种模式通过将状态逻辑从对象中分离出来,并封装到独立的状态类中来实现。每个状态类代表一种特定的状态,拥有自己的一套行为方法。当对象的状态发生变化时,它会切换到另一个状态类,从而改变其行为。
交通信号灯是现实生活中运用状态模式的一个典型例子:红绿黄三种颜色代表了三种不同的状态,每个状态都有特定的行为和持续时间。比如:红色灯表示停止,绿色灯表示通行,黄色灯则提醒驾驶员准备停车。状态模式可以帮助管理系统中信号灯的状态转换逻辑,确保每次只有一种状态是激活的,并且根据预定的时间间隔自动切换到下一个状态。
基本原理
状态模式的核心思想在于:将对象的行为与其状态分离,使得状态的变化能够自然地反映在对象的行为上。这种方法不仅提高了代码的可维护性和扩展性,还促进了面向对象设计原则的应用。无论是开发复杂的游戏系统、构建响应式的用户界面、还是处理复杂的业务流程,状态模式都提供了一种有效的方式来管理对象的状态变化及其相应的行为调整。在状态模式中,一个对象看起来像是改变了它的类一样,因为它根据内部状态的不同而表现出不同的行为。这对于那些需要根据不同状态执行不同操作的应用场景非常有用。
状态模式主要由以下三个核心组件构成。
1、上下文。上下文定义了客户端感兴趣的接口,并维护当前状态的引用。它通过委托给具体的状态对象来执行请求,从而实现状态转换和行为变化。
2、状态接口。定义了一个接口或抽象类,表示所有具体状态的共同行为。该接口通常包含了一些声明的方法,这些方法代表了状态可以执行的操作。
3、具体状态。实现了状态接口的具体类,每种具体状态都负责定义在该状态下应执行的行为。具体状态可以根据需要决定是否要进行状态转换,即在特定条件下改变上下文的当前状态。
基于上面的核心组件,状态模式的实现主要有以下四个步骤。
1、定义状态接口。创建一个接口或抽象类,定义所有具体状态都应该实现的方法。这些方法通常是与上下文相关的操作,比如:执行某个动作,或触发状态转换。
2、创建具体状态类。针对每一个可能的状态,创建具体的实现类。在具体状态类中,我们会实现对应的方法,这些方法定义了在该状态下应该执行的行为,以及如何进行状态转换。
3、实现上下文类。创建上下文类,它持有一个指向当前状态的引用,并提供方法来设置当前状态。上下文类还提供了对外的接口,用于接收来自客户端的请求,并将这些请求委托给当前状态对象来处理。
4、集成状态和上下文。在上下文类中初始化默认状态,并根据业务逻辑的变化动态地切换状态。当上下文接收到请求时,它会调用当前状态对象的相关方法,由具体的状态对象来决定如何响应请求,并可能触发状态转换。
实战代码
在下面的实战代码中,我们使用状态模式模拟了交通信号灯的实现。
首先,我们定义了抽象状态接口CTrafficLightState。它声明了一个纯虚函数Handle,该函数接受一个CTrafficLight类型的引用作为参数。这个接口为所有具体状态提供了统一的行为规范,确保每个状态都能正确处理请求并可能触发状态转换。
接着,我们实现了上下文类CTrafficLight,它维护了一个指向当前状态的指针m_pState。在构造函数中,初始化了初始状态,并在析构函数中删除了该状态对象。SetState方法用于改变当前状态,在设置新状态之前会先释放旧的状态对象。Request方法则是触发当前状态处理逻辑的入口点,如果当前状态有效,则调用其Handle方法。
然后,我们定义了三个具体状态类CRedLightState、CGreenLightState和CYellowLightState,分别表示红灯、绿灯和黄灯三种状态。每个具体状态类都重写了Handle方法,定义了在该状态下应执行的操作,以及如何进行状态转换。比如:当红灯状态被处理时,输出提示信息,并在模拟的时间延迟后切换到绿灯状态。
最后,在main函数中,我们创建了一个初始状态为红灯的CTrafficLight实例,并通过循环调用Request方法模拟了几个周期的交通信号灯变化过程。每次调用Request都会导致当前状态对象处理请求,并根据内部逻辑决定是否需要切换到下一个状态。
#include <iostream>
using namespace std;
// 抽象状态接口
class CTrafficLightState
{
public:
virtual ~CTrafficLightState() = default;
virtual void Handle(class CTrafficLight& trafficLight) = 0;
};
// 上下文:交通信号灯
class CTrafficLight
{
public:
CTrafficLight(CTrafficLightState* pInitial) : m_pState(pInitial) {}
~CTrafficLight()
{
delete m_pState;
}
void SetState(CTrafficLightState* pNewState)
{
delete m_pState;
m_pState = pNewState;
cout << "Traffic Light State Changed" << endl;
}
void Request()
{
if (m_pState != NULL)
{
m_pState->Handle(*this);
}
}
private:
CTrafficLightState* m_pState;
};
// 具体状态:红灯
class CRedLightState : public CTrafficLightState
{
public:
void Handle(CTrafficLight &TrafficLight) override;
};
// 具体状态:绿灯
class CGreenLightState : public CTrafficLightState
{
public:
void Handle(CTrafficLight &TrafficLight) override;
};
// 具体状态:黄灯
class CYellowLightState : public CTrafficLightState
{
public:
void Handle(CTrafficLight &TrafficLight) override;
};
void CRedLightState::Handle(CTrafficLight& TrafficLight)
{
cout << "Red Light: Stop" << endl;
// 假设红灯持续5秒后,切换到绿灯
TrafficLight.SetState(new CGreenLightState());
}
void CGreenLightState::Handle(CTrafficLight& TrafficLight)
{
cout << "Green Light: Go" << endl;
// 假设绿灯持续10秒后,切换到黄灯
TrafficLight.SetState(new CYellowLightState());
}
void CYellowLightState::Handle(CTrafficLight& TrafficLight)
{
cout << "Yellow Light: Prepare To Stop" << endl;
// 假设黄灯持续3秒后,切换到红灯
TrafficLight.SetState(new CRedLightState());
}
int main()
{
// 初始化交通信号灯为红灯状态
CTrafficLight TrafficLight(new CRedLightState());
// 模拟交通信号灯的变化
for (int i = 0; i < 3; ++i)
{
// 处理当前状态,并切换到下一个状态
TrafficLight.Request();
}
return 0;
}
总结
状态模式通过将状态逻辑分离到不同的类中,减少了大量使用if else或switch case语句的需求,使得代码更加简洁和清晰。由于每个状态都被封装在一个独立的类中,这使得添加新状态或修改现有状态变得简单,不会影响到其他状态或业务逻辑。对于那些需要处理多个状态及其转换的应用场景,状态模式提供了一种结构化的方法来组织状态逻辑,使得系统更易于理解和维护。
尽管状态模式简化了单个状态下的行为逻辑,但在某些情况下,状态之间的转换逻辑可能会变得相当复杂,尤其是在状态之间存在多种可能的转换路径时。管理这些转换逻辑,可能需要额外的努力和仔细的设计。另外,频繁的状态切换可能会引入一定的性能开销,因为每次状态切换都需要创建新的状态对象或更新当前状态对象的引用。