一、引言
在软件开发,软件设计过程中,我们常常会遇到对象的行为依赖于其状态的情况。例如,一个任务对象可能有“未开始”、“进行中”、“已完成”等状态,并且在不同状态下执行相同操作会有不同的结果。传统的方法可能会使用大量的条件判断语句来处理不同状态下的行为,这使得代码复杂、难以维护且不易扩展。而状态模式提供了一种优雅的解决方案,它允许对象在内部状态改变时改变其行为,看起来就像是修改了类本身。
二、定义与描述
状态模式属于行为型设计模式。它将对象的行为封装在不同的状态类中,使得对象在不同的状态下表现出不同的行为。在状态模式中,有以下几个主要角色:
上下文(Context):这是一个包含状态对象的类,它定义了客户感兴趣的接口,并维护一个具体状态对象的引用。上下文将操作委托给当前的状态对象来处理。
抽象状态(State):这是一个抽象类或者接口,它定义了一个特定状态下的行为接口。所有具体的状态类都实现这个接口。
具体状态(Concrete State):这些是实现抽象状态接口的类,每个具体状态类实现了与该状态相关的行为。
三、抽象背景
考虑一个简单的订单处理系统。订单有不同的状态,如“已下单”、“已付款”、“已发货”、“已完成”等。在传统的设计中,如果要处理订单在不同状态下的操作(如显示订单状态、执行下一个流程等),可能会有如下的代码:
public class Order {
private String state; public Order(String state) {
this.state = state;
} public void process() {
if ("已下单".equals(state)) {
System.out.println("订单已下单,等待付款");
} else if ("已付款".equals(state)) {
System.out.println("订单已付款,准备发货");
} else if ("已发货".equals(state)) {
System.out.println("订单已发货,等待收货");
} else if ("已完成".equals(state)) {
System.out.println("订单已完成");
}
}
}
这种代码随着状态的增加和操作的复杂化会变得非常难以维护。而使用状态模式可以将每个状态的行为封装到独立的类中,使得代码结构更加清晰。
四、适用场景与现实问题解决
(一)适用场景
对象行为依赖于状态:当一个对象的行为根据其内部状态而改变时,状态模式非常合适。例如,游戏角色在不同的健康状态(满血、受伤、濒死)下有不同的行为(正常移动、缓慢移动、无法移动)。
避免大量条件判断:如果在代码中有大量的基于对象状态的条件判断语句,使用状态模式可以将这些判断分散到各个状态类中,提高代码的可读性和可维护性。
(二)现实问题解决
以自动售货机为例,它有“空闲”、“投币”、“出货”等状态。如果使用传统的条件判断来处理每个状态下的操作(如投币操作在不同状态下的不同行为),代码会变得复杂且容易出错。而采用状态模式,可以清晰地定义每个状态下的行为,并且当需要添加新的状态或者修改现有状态的行为时,只需要在相应的状态类中进行修改,而不会影响到其他部分的代码。
五、状态模式的现实生活的例子
交通信号灯就是一个很好的状态模式的例子。交通信号灯有红、黄、绿三种状态,在不同的状态下,车辆和行人有不同的行为。例如,红灯时车辆停止,绿灯时车辆通行,黄灯时车辆减速准备停车。每个状态都有其对应的行为规则,并且状态之间可以按照一定的顺序转换。
六、初衷与问题解决
初衷是为了更好地处理对象状态相关的行为,使得代码结构更加清晰、易于维护和扩展。通过将状态相关的行为封装到独立的状态类中,解决了传统方式中大量条件判断带来的代码复杂、不易理解和修改困难的问题。
七、代码示例
(一)Java示例
抽象状态接口
interface State {
void handle(Context context);
}
上下文类
class Context {
private State state; public Context(State state) {
this.state = state;
} public void setState(State state) {
this.state = state;
} public void request() {
state.handle(this);
}
}
具体状态类示例(以两种状态为例)
class ConcreteStateA implements State {
@Override
public void handle(Context context) {
System.out.println("ConcreteStateA处理请求"); // 状态转换示例
context.setState(new ConcreteStateB());
}
}class ConcreteStateB implements State {
@Override
public void handle(Context context) {
System.out.println("ConcreteStateB处理请求"); // 状态转换示例
context.setState(new ConcreteStateA());
}
}

测试代码
public class Main {
public static void main(String[] args) {
Context context = new Context(new ConcreteStateA());
context.request();
context.request();
}
}
(二)C++示例
抽象状态类
class State {
public: virtual void handle(Context* context) = 0;
};
上下文类
class Context {
private:
State* state;public: Context(State* state) : state(state) {
} void setState(State* state) {
this.state = state;
} void request() {
state->handle(this);
}
};
具体状态类示例(以两种状态为例)
class ConcreteStateA : public State {
public: void handle(Context* context) override {
std::cout << "ConcreteStateA处理请求" << std::endl; // 状态转换示例
context->setState(new ConcreteStateB());
}
};class ConcreteStateB : public State {
public: void handle(Context* context) override {
std::cout << "ConcreteStateB处理请求" << std::endl; // 状态转换示例
context->setState(new ConcreteStateA());
}
};
测试代码
int main() {
Context* context = new Context(new ConcreteStateA());
context->request();
context->request(); return 0;
}
(三)Python示例
抽象状态类(使用抽象基类实现)
from abc import ABC, abstractmethodclass State(ABC):
@abstractmethod def handle(self, context):
pass
上下文类
class Context: def __init__(self, state):
self.state = state def setState(self, state):
self.state = state def request(self):
self.state.handle(self)
具体状态类示例(以两种状态为例)
class ConcreteStateA(State): def handle(self, context): print("ConcreteStateA处理请求") # 状态转换示例
context.setState(ConcreteStateB())class ConcreteStateB(State): def handle(self, context): print("ConcreteStateB处理请求")
# 状态转换示例
```js
context.setState(ConcreteStateA())
测试代码
```js
if __name__ == "__main__":
context = Context(ConcreteStateA())
context.request();
context.request();
(四)Go示例
抽象状态接口
type State interface {
handle(context *Context)
}
上下文结构体
type Context struct {
state State
}func (c *Context) setState(state State) {
c.state = state
}func (c *Context) request() {
c.state.handle(c)
}
具体状态结构体示例(以两种状态为例)
type ConcreteStateA struct{
}func (c ConcreteStateA) handle(context *Context) {
println("ConcreteStateA处理请求") // 状态转换示例
context.setState(ConcreteStateB{
})
}type ConcreteStateB struct{
}func (c ConcreteStateB) handle(context *Context) {
println("ConcreteStateB处理请求") // 状态转换示例
context.setState(ConcreteStateA{
})
}
测试代码
func main() {
context := &Context{
state: ConcreteStateA{
}}
context.request()
context.request()
}
八、状态模式的优缺点
(一)优点
提高代码的可维护性:将每个状态的行为封装到独立的类中,使得代码结构清晰,易于理解和修改。当需要添加新的状态或者修改现有状态的行为时,只需要在相应的状态类中进行操作,而不会影响到其他部分的代码。
符合开闭原则:对扩展开放,对修改关闭。可以轻松地添加新的状态类而不需要修改原有的上下文类和其他状态类的代码。
增强代码的可读性:避免了大量的条件判断语句,使得代码逻辑更加直观。
(二)缺点
类的数量增加:每个状态都需要一个对应的状态类,这可能会导致类的数量过多,特别是在状态比较复杂或者状态数量较多的情况下。
状态转换逻辑分散:状态之间的转换逻辑分布在各个状态类中,如果状态转换比较复杂,可能会导致代码的理解和维护成本增加。
九、状态模式的升级版
一种常见的升级版是引入状态机框架。状态机框架可以更加方便地管理状态之间的转换关系,提供了统一的状态转换规则配置方式。例如,在一些游戏开发中使用的有限状态机(FSM)框架,它可以预先定义好所有的状态和状态之间的转换条件,然后在游戏运行过程中根据实际情况进行状态转换。这样不仅提高了状态管理的效率,还使得整个状态处理流程更加清晰、可控。