一、什么是状态模式
状态模式是一种行为设计模式,使用状态模式,能够在一个对象内部状态变化时改变它的行为,使其看起来就像改变自身的类一样。
核心点:区分事物内部的状态,把事物的每种状态都封装成单独的类,跟此状态有关的行为都被封装在这个类的内部,当对象的状态改变时,它的行为也会随之改变。
二、状态模式的主要构成
- 环境类(Context)角色:也叫上下文,定义类客户端需要的接口,维护了一个当前状态,并将状态相关的操作委托给当前的状态对象来处理;
- 抽象状态(State)角色:定义一个状态接口,用于封装状态所对应的行为;
- 具体状态(Concrete State)角色:实现抽象状态对应的行为,用于切换状态
三、状态模式代码示例
假如有一个抽奖奖励场景,奖励存在多种状态,分别为未激活状态、已激活状态、已暂停状态和已结束状态,各个状态之间会根据相关方法或者时间触发到转换到其他状态,状态转换规则如下图所示
处于未激活状态,它会根据开始时间自动激活奖励,转移到已激活状态,处于已激活状态的奖励,管理员可以将抽奖活动进行暂停,此时奖励会进入已暂停状态,处于已暂停状态的奖励,又可以手动开启,进入已激活状态;处于已激活状态,会随着奖品的发放完成,进入到已结束状态,在不同的状态下,奖励对应的行为也是不同的。
假如没有使用状态模式,我们通常的做法是使用多个条件运算符if else或switch来实现,代码示例如下:
public class Reward {
private enum State {
INACTIVE, ACTIVE, PAUSED, FINISHED
}
private State currentState;
public Reward() {
currentState = State.INACTIVE; // 初始状态为未激活
}
public void activate() {
switch (currentState) {
case INACTIVE:
System.out.println("奖励已激活");
currentState = State.ACTIVE;
break;
case ACTIVE:
System.out.println("奖励已激活,无需再次激活");
break;
case PAUSED:
System.out.println("奖励已激活");
break;
case FINISHED:
System.out.println("奖励已结束,无法激活");
break;
}
}
public void pause() {
switch (currentState) {
case INACTIVE:
System.out.println("奖励未激活,不能暂停");
break;
case ACTIVE:
System.out.println("奖励已暂停");
currentState = State.PAUSED;
break;
case PAUSED:
System.out.println("奖励已暂停,无需再次暂停");
break;
case FINISHED:
System.out.println("奖励已结束,无法暂停");
break;
}
}
public void finish() {
switch (currentState) {
case INACTIVE:
System.out.println("奖励已结束");
break;
case ACTIVE:
System.out.println("奖励已结束");
currentState = State.FINISHED;
break;
case PAUSED:
System.out.println("奖励已结束");
currentState = State.FINISHED;
break;
case FINISHED:
System.out.println("奖励已结束,无需再次结束");
break;
}
}
public static void main(String[] args) {
Reward reward = new Reward();
// 未激活状态
reward.activate();
reward.pause();
reward.finish();
// 激活状态
reward.activate();
reward.pause();
reward.finish();
// 暂停状态
reward.pause();
reward.finish();
// 结束状态
reward.finish();
reward.activate();
reward.pause();
reward.finish();
}
}
存在的问题:由于我们预先定义了枚举类的一些状态,后面会随着业务规则变动,又会增加状态以及状态之间的转换规则,则传统的if else结构,显得扩展性不足,代码会变得臃肿不好维护。
使用状态模式实现如下:
public interface RewardState {
void activate();
void pause();
void finish();
}
public class InactiveState implements RewardState{
@Override
public void activate() {
System.out.println("奖励已激活");
}
@Override
public void pause() {
System.out.println("奖励未激活,不能暂停");
}
@Override
public void finish() {
System.out.println("奖励未激活,不能结束");
}
}
public class ActiveState implements RewardState{
@Override
public void activate() {
System.out.println("奖励已激活,无需再次激活");
}
@Override
public void pause() {
System.out.println("奖励已暂停");
}
@Override
public void finish() {
System.out.println("奖励已结束");
}
}
public class PausedState implements RewardState{
@Override
public void activate() {
System.out.println("奖励已激活");
}
@Override
public void pause() {
System.out.println("奖励已暂停,无需再次暂停");
}
@Override
public void finish() {
System.out.println("奖励已结束");
}
}
public class FinishedState implements RewardState{
@Override
public void activate() {
System.out.println("奖励已结束,无法再次激活");
}
@Override
public void pause() {
System.out.println("奖励已结束,无法暂停");
}
@Override
public void finish() {
System.out.println("奖励已结束,无需再次结束");
}
}
public class RewardContext {
private RewardState state;
public RewardContext() {
state = new InactiveState(); // 初始状态为未激活
}
public void setState(RewardState state) {
this.state = state;
}
public void activate() {
state.activate();
}
public void pause() {
state.pause();
}
public void finish() {
state.finish();
}
}
// Demo测试
public class Main {
public static void main(String[] args) {
RewardContext context = new RewardContext();
// 未激活状态
context.activate();
context.pause();
context.finish();
// 激活状态
context.setState(new ActiveState());
context.activate();
context.pause();
context.finish();
// 暂停状态
context.setState(new PausedState());
context.activate();
context.pause();
context.finish();
// 结束状态
context.setState(new FinishedState());
context.activate();
context.pause();
context.finish();
}
}
执行结果:
四、状态模式的应用
- 订单处理系统,订单可以处于不同的状态,如已下单、待支付、已支付、待发货、已发货、已完成等。状态模式可以用来管理订单之间状态的转换和对应的执行的操作。
- 工作流引擎,在工作流管理中,任务可以处于不同的状态,如待处理、处理中、处理完成等等。
- 游戏角色状态,游戏中的人物角色可以处于不同的状态,例如站立状态、行走状态、跑动状态、攻击状态等
- 电梯控制系统,电梯可以处于不同的状态,例如停止状态、运行状态、上行、下行、故障等等。
五、状态模式优缺点
优点:
- 代码可读性好,状态之间的转换封装到了具体类中,可以清晰管理状态之间的转换;
- 可维护性好,扩展性强,如添加和删除状态比较方便,不会影响原有状态类的代码;
- 符合开闭原则: 状态模式使得状态类可以独立变化和扩展,符合开闭原则,对修改封闭,对扩展开放。
缺点:
4. . 增加代码中对象和类的个数,增加了系统实现的复杂性;
- 状态间的转换: 状态之间的转换可能比较复杂,需要仔细设计状态之间的转换关系;
- 状态模式的适用场景有限: 如果一个对象的状态很少改变,或者状态转换比较简单,使用状态模式可能会显得过于繁琐。
参考:订单及其状态机的设计实现