重拾设计模式--状态模式
文章目录
- 状态模式(State Pattern)概述
- 状态模式UML图
- 作用:
- 状态模式的结构
- 环境(Context)类:
- 抽象状态(State)类:
- 具体状态(Concrete State)类:
- C++ 代码示例1
- C++示例代码2
- 应用场景
- 游戏开发领域
- 图形用户界面(GUI)系统
- 工作流系统和业务流程管理
- 网络协议和通信系统
状态模式(State Pattern)概述
定义:
状态模式是一种行为型设计模式,它允许对象在其内部状态改变时改变它的行为。就好像对象看起来修改了它的类一样,通过将不同状态对应的行为封装到不同的状态类中,让一个对象在不同的状态下可以表现出不同的行为,而这些行为的切换是由对象的状态变化来驱动的。
状态模式UML图
作用:
提高代码的可维护性和可扩展性:当一个对象有多种状态,且每种状态下行为差异较大时,如果使用大量的条件判断语句(如if-else或者switch语句)来处理不同状态下的行为,代码会变得复杂且难以维护。而状态模式把不同状态的行为分散到各个对应的状态类中,后续添加新状态或者修改某个状态下的行为就比较方便,只需对相应的状态类进行操作,不会影响到其他部分的代码。
符合开闭原则:对扩展开放,对修改关闭。例如在一个游戏角色有多种状态(站立、行走、攻击等)的场景中,若要新增一种 “跳跃” 状态以及对应的行为,只需要新增一个 “跳跃” 状态类实现相关行为逻辑,而不用去大规模修改原有的代码结构,原有的状态类和使用状态的对象代码都可以保持相对稳定。
增强代码的可读性:不同状态下的行为逻辑清晰地封装在各自的状态类中,阅读代码时可以更直观地了解每个状态对应的具体操作,相比于把所有状态相关行为混杂在一个类里的写法,结构更加清晰明了。
状态模式的结构
环境(Context)类:
它定义了客户端感兴趣的接口,并且维护一个具体状态类的实例,这个实例代表了当前对象所处的状态。环境类的行为会受到其内部状态对象的影响,它会将请求委托给当前状态对象来处理。
抽象状态(State)类:
它定义了一个接口,这个接口包含了那些在不同具体状态下对象可能执行的方法,所有的具体状态类都要实现这个抽象状态类所定义的接口,来提供各自状态下的具体行为逻辑。
具体状态(Concrete State)类:
实现了抽象状态类中定义的接口,针对具体的某一种状态,提供了该状态下相应行为的具体实现。每个具体状态类实现的行为会根据具体业务需求而不同,当环境类的状态切换到某个具体状态时,环境类发出的请求就会由对应的这个具体状态类来处理。
C++ 代码示例1
以下以一个简单的电梯控制系统为例来展示状态模式的代码实现。电梯有几种不同的状态,比如静止、上升、下降等,每种状态下对于外部的请求(如按楼层按钮等)会有不同的响应行为。
#include <iostream>
// 抽象状态类
class ElevatorState
{
public:
virtual void open() = 0;
virtual void close() = 0;
virtual void run() = 0;
virtual void stop() = 0;
};
// 具体状态类:静止状态
class StoppedState : public ElevatorState
{
public:
void open() override
{
std::cout << "电梯门打开,当前处于静止状态。" << std::endl;
}
void close() override
{
std::cout << "电梯门关闭,当前处于静止状态,准备运行。" << std::endl;
}
void run() override
{
std::cout << "电梯从静止状态开始运行。" << std::endl;
}
void stop() override
{
std::cout << "电梯已经处于静止状态,无需再次停止。" << std::endl;
}
};
// 具体状态类:上升状态
class RisingState : public ElevatorState
{
public:
void open() override
{
std::cout << "电梯正在上升,不能开门。" << std::endl;
}
void close() override
{
std::cout << "电梯正在上升,门已关闭。" << std::endl;
}
void run() override
{
std::cout << "电梯继续上升。" << std::endl;
}
void stop() override
{
std::cout << "电梯上升过程中停止。" << std::endl;
// 假设停止后切换到静止状态,可以在这里做相应状态切换逻辑,比如通知环境类切换状态
}
};
// 具体状态类:下降状态
class FallingState : public ElevatorState
{
public:
void open() override
{
std::cout << "电梯正在下降,不能开门。" << std::endl;
}
void close() override
{
std::cout << "电梯正在下降,门已关闭。" << std::endl;
}
void run() override
{
std::cout << "电梯继续下降。" << std::endl;
}
void stop() override
{
std::cout << "电梯下降过程中停止。" << std::endl;
// 同样,停止后可考虑切换状态逻辑
}
};
// 环境类:电梯
class Elevator
{
private:
ElevatorState* currentState;
public:
Elevator()
{
currentState = new StoppedState();
}
void setCurrentState(ElevatorState* state)
{
currentState = state;
}
void open()
{
currentState->open();
}
void close()
{
currentState->close();
}
void run()
{
currentState->run();
}
void stop()
{
currentState->stop();
}
};
int main()
{
Elevator elevator;
elevator.open();
elevator.close();
elevator.run();
elevator.stop();
RisingState risingState;
elevator.setCurrentState(&risingState);
elevator.open();
elevator.close();
elevator.run();
elevator.stop();
return 0;
}
C++示例代码2
#include<iostream>
#include<list>
#include<string>
using namespace std;
class Andy;//男主
class State//状态
{
public:
string m_satedes;//描述当前的状态
public:
virtual void Fbegin(){};//前期状态
virtual void Fmid(){};//中期状态
virtual void Fend(){};//末期状态
virtual void CurrentState(Andy*p_andy){};//调用当前状态,统一的接口,然后各个状态子类去调用自己的函数
};
class Andy
{
private:
State *m_state; //目前状态
int m_years; //时间
public:
Andy(State *state): m_state(state), m_years(0) {}
~Andy() { delete m_state; }
int GetYears() { return m_years; }
void SetYears(int years) { m_years = years; }
void SetState(State *state) { delete m_state; m_state = state; }
void GetState() { m_state->CurrentState(this); }
};
//服刑末期
class State_End:public State
{
public:
State_End()
{
m_satedes = "挖洞逃出来了";
}
void FEnd(Andy *p_andy)
{
cout<<"第"<<p_andy->GetYears()<<"年"<<m_satedes<<endl;
}
void CurrentState(Andy *p_andy)
{
FEnd(p_andy);
}
};
//服刑中期
class State_Mid:public State
{
public:
State_Mid()
{
m_satedes = "服刑中期,每天帮监狱长做假账,顺便开始夜里挖洞";
}
void FMid(Andy *p_andy)
{
if(p_andy->GetYears()<28)
{
cout<<"第"<<p_andy->GetYears()<<"年"<<m_satedes<<endl;
}
else
{
p_andy->SetState(new State_End());
p_andy->GetState();
}
}
void CurrentState(Andy *p_andy)
{
FMid(p_andy);
}
};
//服刑前期
class State_begin:public State
{
public:
State_begin()
{
m_satedes = "男主Andy刚进监狱,处处受人欺负";
}
void Fbegin(Andy *p_andy)
{
if(p_andy->GetYears()<5)
{
cout<<"第"<<p_andy->GetYears()<<"年"<<m_satedes<<endl;
}
else
{
p_andy->SetState(new State_Mid());
p_andy->GetState();
}
}
void CurrentState(Andy *p_andy)
{
Fbegin(p_andy);
}
};
int main()
{
Andy *p = new Andy(new (State_begin));
for(int i = 1; i < 40; ++i)
{
p->SetYears(i);
p->GetState();
}
delete p;
return 0;
}
应用场景
游戏开发领域
角色状态管理:游戏中的角色通常有多种状态,如站立、行走、奔跑、跳跃、攻击、受伤、死亡等。使用状态模式可以为每个状态创建一个单独的类,在这些类中实现角色在该状态下的行为,如移动速度、动画播放、可执行的操作等。例如,当角色处于攻击状态时,其移动速度可能会降低,并且会播放攻击动画,同时能够触发攻击相关的逻辑,如伤害计算。
游戏场景状态切换:游戏场景也可以有不同的状态,像游戏的主菜单场景、游戏进行中的场景、暂停场景、游戏结束场景等。每个场景状态都有自己的一套显示内容、交互逻辑和更新机制。通过状态模式,可以方便地在不同场景状态之间切换,并且清晰地管理每个状态下的资源加载、渲染和事件处理。
图形用户界面(GUI)系统
窗口状态管理:一个窗口可能有多种状态,如最小化、最大化、正常、隐藏等。在不同状态下,窗口的显示方式、对用户操作的响应等都不同。利用状态模式,可以将每个状态对应的行为封装到相应的状态类中。例如,当窗口处于最小化状态时,它可能只在任务栏显示一个图标,并且点击图标时的操作(如恢复窗口大小)与窗口处于正常状态时的操作(如拖动边框改变大小)是不同的。
按钮状态控制:按钮有正常、鼠标悬停、按下等状态。在不同状态下,按钮的外观(如颜色、样式)和行为(如触发的事件)不同。状态模式可以让开发者轻松地定义每个状态下按钮的渲染逻辑和事件响应逻辑,使得按钮的状态管理更加清晰和易于维护。
工作流系统和业务流程管理
订单处理流程:在电商系统中,订单有多种状态,如已创建、已付款、已发货、已签收、已退款等。每个状态下的订单处理逻辑不同,例如在 “已付款” 状态下,系统会通知仓库准备发货;在 “已发货” 状态下,系统会提供物流信息查询等服务。通过状态模式,可以把订单在不同状态下的处理逻辑封装在对应的状态类中,方便业务流程的扩展和维护。
审批流程:企业中的审批流程也存在多种状态,如提交审批、部门主管审批中、高层领导审批中、审批通过、审批拒绝等。对于每个状态,都有相应的操作和流转规则,比如在 “部门主管审批中” 状态下,部门主管可以查看申请内容并进行审批操作,审批通过后状态会切换到 “高层领导审批中”。使用状态模式可以清晰地实现这种复杂审批流程的状态管理和操作逻辑。
网络协议和通信系统
TCP 连接状态管理:在 TCP 协议中,连接有多种状态,如 LISTEN(监听)、SYN - SENT(同步已发送)、SYN - RECEIVED(同步收到)、ESTABLISHED(已建立)、FIN - WAIT - 1(终止等待 1)等。在不同状态下,网络设备(如服务器和客户端)的行为和数据处理方式不同。状态模式可以用于实现 TCP 连接状态的管理,将每个状态对应的数据包处理、连接维护等操作封装在相应的状态类中,使得网络通信的状态处理更加清晰和可靠。
设备通信状态:在物联网系统中,设备之间的通信可能会出现多种状态,如连接正常、连接中断、数据传输中、等待响应等。对于每种状态,可以使用状态模式来定义设备在该状态下的通信行为,如重新连接策略、数据缓存和发送方式等,以确保设备之间的通信稳定和高效。