状态模式定义
状态模式,英文名State Pattern,它允许一个对象其内部状态改变时改变它的行为,它能够通过调用模式接口中定义的方法来切换策略。
状态模式是一种行为模式,用于封装同一对象的不同行为,可以是对象在运行时更改其行为的更简洁的方式,而无需求助于条件语句,从而提高可维护性。
状态模式作用
将业务模型抽象成一个有限状态机,减少大量的switch-case和if-else的使用,使得代码逻辑清晰,结构规整。
状态模式角色
状态模式包含以下主要角色,如下图所示:
1.环境类Context角色
环境类Context:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
2.抽象状态State角色
定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
3.具体状态Concrete State角色
实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
状态模式案例
可以用用“状态模式”设计一个多线程的状态转换程序,多线程存在 5 种状态:分别为新建状态、就绪状态、运行状态、阻塞状态和死亡状态。
各个状态当遇到相关方法调用,或事件触发时会转换到其他状态转换。
如下图所示:
在先定义一个抽象状态类(TheadState),然后为图 3 所示的每个状态设计一个具体状态类,它们是新建状态(New)、就绪状态(Runnable )、运行状态(Running)、阻塞状态(Blocked)和死亡状态(Dead),每个状态中有触发它们转变状态的方法,环境类(ThreadContext)中先生成一个初始状态(New),并提供相关触发方法,图 4 所示是线程状态转换程序的结构图。
代码示例如下:
第一步:创建环境类(Context角色)
//环境类 class ThreadContext { private ThreadState state; ThreadContext() { state = new New(); } public void setState(ThreadState state) { this.state = state; } public ThreadState getState() { return state; } public void start() { ((New) state).start(this); } public void getCPU() { ((Runnable) state).getCPU(this); } public void suspend() { ((Running) state).suspend(this); } public void stop() { ((Running) state).stop(this); } public void resume() { ((Blocked) state).resume(this); } }
第二步:创建抽象状态(State角色)
//抽象状态类:线程状态 abstract class ThreadState { protected String stateName; //状态名 }
第三步:创建具体状态(Concrete State角色)
一共分为5大状态,如下所示:
//具体状态类:新建状态 class New extends ThreadState { public New() { stateName = "新建状态"; System.out.println("当前线程处于:新建状态."); } public void start(ThreadContext hj) { System.out.print("调用start()方法-->"); if (stateName.equals("新建状态")) { hj.setState(new Runnable()); } else { System.out.println("当前线程不是新建状态,不能调用start()方法."); } } } //具体状态类:就绪状态 class Runnable extends ThreadState { public Runnable() { stateName = "就绪状态"; System.out.println("当前线程处于:就绪状态."); } public void getCPU(ThreadContext hj) { System.out.print("获得CPU时间-->"); if (stateName.equals("就绪状态")) { hj.setState(new Running()); } else { System.out.println("当前线程不是就绪状态,不能获取CPU."); } } } //具体状态类:运行状态 class Running extends ThreadState { public Running() { stateName = "运行状态"; System.out.println("当前线程处于:运行状态."); } public void suspend(ThreadContext hj) { System.out.print("调用suspend()方法-->"); if (stateName.equals("运行状态")) { hj.setState(new Blocked()); } else { System.out.println("当前线程不是运行状态,不能调用suspend()方法."); } } public void stop(ThreadContext hj) { System.out.print("调用stop()方法-->"); if (stateName.equals("运行状态")) { hj.setState(new Dead()); } else { System.out.println("当前线程不是运行状态,不能调用stop()方法."); } } } //具体状态类:阻塞状态 class Blocked extends ThreadState { public Blocked() { stateName = "阻塞状态"; System.out.println("当前线程处于:阻塞状态."); } public void resume(ThreadContext hj) { System.out.print("调用resume()方法-->"); if (stateName.equals("阻塞状态")) { hj.setState(new Runnable()); } else { System.out.println("当前线程不是阻塞状态,不能调用resume()方法."); } } } //具体状态类:死亡状态 class Dead extends ThreadState { public Dead() { stateName = "死亡状态"; System.out.println("当前线程处于:死亡状态."); } }
第四步:测试状态转换
public class ScoreStateTest { public static void main(String[] args) { ThreadContext context = new ThreadContext(); context.start(); context.getCPU(); context.suspend(); context.resume(); context.getCPU(); context.stop(); } }
运行结果如下:
当前线程处于:新建状态. 调用start()方法-->当前线程处于:就绪状态. 获得CPU时间-->当前线程处于:运行状态. 调用suspend()方法-->当前线程处于:阻塞状态. 调用resume()方法-->当前线程处于:就绪状态. 获得CPU时间-->当前线程处于:运行状态. 调用stop()方法-->当前线程处于:死亡状态.
状态模式应用
通常在以下情况下可以考虑使用状态模式,大致分为两类:
第一类:当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式;
第二类:一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
陈睿mikechen
10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。
关注「mikechen」公众号,获取更多技术干货!
后台回复【面试】即可获取《史上最全阿里Java面试题总结》,后台回复【架构】,即可获取《阿里架构师进阶专题全部合集》