类图: 状态设计模式类图
一.什么是状态模式?
状态模式(State Pattern)是一种行为型设计模式,允许对象在其内部状态改变时改变其行为,看起来就像对象改变了其类。状态模式通过将状态封装为独立的类,使得对象的行为能够根据内部状态动态地改变。
在状态模式中,Context(环境类)持有一个指向State(状态接口)的引用对象,并通过该对象来进行状态切换。具体的状态通过实现状态接口的不同子类来进行封装。
二.状态模式的结构
- Context(环境类):用于维护当前状态,并在特定操作时切换状态。它持有一个State对象的引用,用来对状态进行操作。
- State(状态接口):定义了一个接口,声明了每个具体状态需要实现的行为。
- ConcreteStateA 和 ConcreteStateB(具体状态类):实现State接口的不同子类,表示对象在不同状态下的行为。
三.状态模式的应用场景
- 对象行为依赖于状态,且状态在运行时变化:如电梯的状态(上升、下降、停止)、游戏角色的状态(跑步、跳跃、静止)。
- 条件语句过多:当对象中有大量if-else或switch语句判断状态时,可以考虑使用状态模式进行优化。
四.状态模式的优缺点
-
优点:
- 封装状态转换:将状态与相关行为封装在一个类中,便于维护和扩展。
- 避免条件判断:通过多态替代了条件判断,代码更具可读性和扩展性。
-
缺点:
- 类数量增加:每个状态需要一个类,可能会导致类数量较多。
五.状态模式的C++实现
#include <iostream>
#include <memory>
// 前置声明,避免循环引用
class State;
// 环境类(Context)
class Context {
private:
std::shared_ptr<State> state; // 当前状态
public:
// 设置当前状态
void setState(std::shared_ptr<State> newState) {
state = newState;
}
// 执行请求,调用当前状态的Handle方法
void request();
};
// 状态接口(State)
class State {
public:
virtual void handle(Context& context) = 0; // 定义纯虚函数,具体状态类需实现
};
// 具体状态类 A(ConcreteStateA)
class ConcreteStateA : public State {
public:
void handle(Context& context) override {
std::cout << "ConcreteStateA handling request.\n";
// 状态切换:可以切换到状态 B
context.setState(std::make_shared<ConcreteStateB>());
}
};
// 具体状态类 B(ConcreteStateB)
class ConcreteStateB : public State {
public:
void handle(Context& context) override {
std::cout << "ConcreteStateB handling request.\n";
// 状态切换:可以切换回状态 A
context.setState(std::make_shared<ConcreteStateA>());
}
};
// 环境类方法的实现
void Context::request() {
if (state) {
state->handle(*this); // 调用当前状态的 handle 方法
}
}
// 测试代码
int main() {
Context context;
// 初始化状态为 A
context.setState(std::make_shared<ConcreteStateA>());
// 执行多次请求,测试状态切换
context.request(); // ConcreteStateA 处理请求,然后切换到 ConcreteStateB
context.request(); // ConcreteStateB 处理请求,然后切换到 ConcreteStateA
context.request(); // ConcreteStateA 处理请求,然后切换到 ConcreteStateB
return 0;
}
六.状态模式的JAVA实现
// 抽象状态类
abstract class State {
public abstract void Handle(Context context);
}
// 具体状态A
class ConcreteStateA extends State {
@Override
public void Handle(Context context) {
System.out.println("ConcreteStateA: Handling request and transitioning to ConcreteStateB.");
context.setState(new ConcreteStateB());
}
}
// 具体状态B
class ConcreteStateB extends State {
@Override
public void Handle(Context context) {
System.out.println("ConcreteStateB: Handling request and transitioning to ConcreteStateA.");
context.setState(new ConcreteStateA());
}
}
// 环境类 (Context)
class Context {
private State state;
public Context(State state) {
this.state = state;
}
public void setState(State state) {
this.state = state;
}
public void Request() {
if (state != null) {
state.Handle(this);
}
}
}
// 客户端代码
public class StatePatternDemo {
public static void main(String[] args) {
// 初始状态为 ConcreteStateA
Context context = new Context(new ConcreteStateA());
// 调用多次 Request,观察状态切换
context.Request(); // 从 A 切换到 B
context.Request(); // 从 B 切换到 A
context.Request(); // 再次从 A 切换到 B
}
}
七.代码解释
- Context 类:
- Context类持有一个指向State的指针(使用智能指针std::shared_ptr来管理状态对象的生命周期)。
- 它包含一个request()方法,用于请求当前状态处理相应的操作。
- setState()方法用于切换状态,方便在运行时改变state的引用指向不同的State对象。
- State 接口:
- State接口中定义了一个纯虚函数handle(),表示不同状态下的行为。
- Context通过调用state->handle(*this)来执行当前状态的具体行为。
- ConcreteStateA 和 ConcreteStateB:
- ConcreteStateA和ConcreteStateB是State接口的具体实现类,每个类都定义了各自的handle()方法。
- 在ConcreteStateA::handle()中,Context的状态被切换为ConcreteStateB,而在ConcreteStateB::handle()中,状态切换回ConcreteStateA。
- 这种状态切换体现了状态模式的核心思想,即状态内部控制状态转换,客户端不需要关心状态如何切换。
- 测试代码:
- 在main函数中,创建Context对象context,并将初始状态设为ConcreteStateA。
- 通过多次调用request()方法,可以看到状态在ConcreteStateA和ConcreteStateB之间切换,体现出对象行为随状态变化而变化的特点。
八.总结
状态模式是一种非常适合解决对象行为依赖于状态变化的设计模式。通过将状态封装为独立的类,状态模式使得代码更加清晰和易于维护,避免了条件判断语句的冗余和复杂性。每个具体状态类不仅实现了各自的行为,还决定了状态切换的逻辑,从而让状态之间的转换更为灵活。
关键点:
- 状态模式封装了对象内部的状态变化逻辑,使得对象的行为可以动态改变。
- 通过多态,状态模式避免了条件判断,使代码更具可扩展性。
- 状态切换由具体状态类控制,客户端只需与Context交互,降低了耦合度。