状态模式
1、状态模式介绍
状态模式(State)是一种行为型设计模式,当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。(可以说当到饭点了,你就会主动去找饭吃)
1.1 存在问题
通常情况下,一个对象的行为会随着其内部状态的改变而发生变化,这些行为通常被包含在相应的if...else
语句中,导致代码难以维护和扩展。
1.2 解决问题
适用状态模式判断状态是否改变。
状态模式的思想是将每种可能的状态都封装成一个类,因此可以在不改变原有代码的前提下动态地改变对象的行为。状态模式提供了一种简单的实现方式,即通过定义一个state接口,再定义具体的state子类,Context类中持有一个state的引用,在不同状态下,分别委托给不同的state处理相应的请求。这种实现方式将原来的大类拆分成多个小类,使得系统更加灵活、易于扩展,符合开闭原则。
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简单,那就没必要用状态模式了。
1.3 状态模式结构图
- State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。
/** * @author Shier * CreateTime 2023/5/7 10:39 * 抽象状态类 */ public abstract class State { public abstract void handle(Context context); }
Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态。
/** * @author Shier * CreateTime 2023/5/7 10:41 * */ public class Context { private State state; /** * 初始化当前状态 * @param state */ public Context(State state) { this.state = state; } public State getState() { return state; } public void setState(State state) { this.state = state; System.out.println("当前状态:"+this.state.getClass().getName()); } /** * 对请求做处理,并设置下一个状态 */ public void request(){ this.state.handle(this); } }
ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为。
/** * @author Shier * CreateTime 2023/5/7 10:40 * 具体状态类A */ public class ConcreteStateA extends State{ /** * 设置ConcreteStateA的下一个状态是ConcreteStateB * @param context */ @Override public void handle(Context context) { context.setState(new ConcreteStateB()); } }
/** * @author Shier * CreateTime 2023/5/7 10:45 * 具体状态类B */ public class ConcreteStateB extends State { /** * 设置ConcreteStateB的下一个状态是ConcreteStateA * @param context */ @Override public void handle(Context context) { context.setState(new ConcreteStateA()); } }
客户端发起请求调用
/** * @author Shier * CreateTime 2023/5/7 10:46 */ public class ClientTest { public static void main(String[] args) { // 初始状态为ConcreteStateA Context context = new Context(new ConcreteStateA()); // 不断请求,不断改变请求状态 context.request(); context.request(); context.request(); } }
- 出结果:
2、具体案例说明状态模式
案例:不同的工作时间做不同的事情
不使用状态模式
/** * @author Shier * CreateTime 2023/5/7 11:01 * 工作类 */ public class Work { // 时间 private int hour; // 是否完成工作任务 private boolean workFinished = false; public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } public boolean getWorkFinished() { return workFinished; } public void setWorkFinished(boolean workFinished) { this.workFinished = workFinished; } /** * 工作时间段 */ public void workTime() { if (hour < 12) { System.out.println("当前时间" + hour + "点,上午工作,精神百倍。"); } else if (hour < 13) { System.out.println("当前时间" + hour + "点,饿了,午饭,犯困,午休。"); } else if (hour < 17) { System.out.println("当前时间" + hour + "点,下午状态还可以,继续敲代码。"); } else { if (workFinished) { System.out.println("当前时间" + hour + "点,下班回家了!!,愉快结束一天。"); } else { if (hour < 21) { System.out.println("当前时间" + hour + "点,又开始加班,疲累之极,**加班。"); } else { System.out.println("当前时间" + hour + "点,睡觉时间到了,躺床就睡着。"); } } } } }
客户端
/** * @author Shier * CreateTime 2023/5/7 11:09 */ public class WorkClient { public static void main(String[] args) { Work work = new Work(); // 早上 work.setHour(9); work.workTime(); work.setHour(11); work.workTime(); // 中午 work.setHour(12); work.workTime(); //下午 work.setHour(13); work.workTime(); work.setHour(14); work.workTime(); work.setHour(17); //工作未完成 work.setWorkFinished(false); // 加班 work.workTime(); work.setHour(19); work.workTime(); work.setHour(22); work.workTime(); } }
结果显示:
有没有发现什么问题?workTime()这个方法已经违背了开闭原则了,每次都要修改这个方法才得以扩展新的功能
2.2 使用状态模式
具体的结构类图
State类:
/** * @author Shier * CreateTime 2023/5/7 12:02 * 状态类 */ public abstract class State { public abstract void workTime(Work work); }
Work工作类:
/** * @author Shier * CreateTime 2023/5/7 11:01 * 工作类 */ public class Work { /** * 时间点 */ private int hour; /** * 是否完成工作任务-是否到达下班条件 */ private boolean workFinished = false; /** * 当前状态-设置下一个状态 */ private State currentState; /** * 初始化状态-上午 */ public Work() { currentState = new ForenoonState(); } /** * 工作时间段-显示当前状态,并切换到下一个状态 */ public void workTime() { this.currentState.workTime(this); } public State getCurrentState() { return currentState; } public void setCurrentState(State currentState) { this.currentState = currentState; } public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } public boolean getWorkFinished() { return workFinished; } public void setWorkFinished(boolean workFinished) { this.workFinished = workFinished; } }
早上具体状态:
/** * @author Shier * CreateTime 2023/5/7 12:02 * 早上具体状态 */ public class ForenoonState extends State { @Override public void workTime(Work work) { if (work.getHour() < 12) { System.out.println("当前时间" + work.getHour() + "点,上午工作,精神百倍。"); } else { // 超过12点就转入中午状态 work.setCurrentState(new NoonState()); work.workTime(); } } }
中午具体状态:
/** * @author Shier * CreateTime 2023/5/7 12:10 * 中午状态 */ public class NoonState extends State { @Override public void workTime(Work work) { if (work.getHour() < 13) { System.out.println("当前时间" + work.getHour() + "点,饿了,午饭,犯困,午休。"); } else { // 超过13点就转入下午工作状态 work.setCurrentState(new AfternoonState()); work.workTime(); } } }
下午具体状态:
/** * @author Shier * CreateTime 2023/5/7 12:12 * 下午具体状态 */ public class AfternoonState extends State { @Override public void workTime(Work work) { if (work.getHour() < 17) { System.out.println("当前时间" + work.getHour() + "点,下午状态还可以,继续敲代码。"); } else { // 超时17点就进去傍晚工作时间点 work.setCurrentState(new EveingState()); work.workTime(); } } }
任务完成,按时下班状态类
/** * @author Shier * CreateTime 2023/5/7 12:15 * 按时下班 */ public class RestState extends State { @Override public void workTime(Work work) { System.out.println("当前时间:" + work.getHour() + "点,下班回家咯!!"); } }
工作任务未完成,加班“累”:
/** * @author Shier * CreateTime 2023/5/7 12:13 * 具体加班类 */ public class EveingState extends State { @Override public void workTime(Work work) { if (work.getWorkFinished()) { // 工作完成,下班 work.setCurrentState(new RestState()); work.workTime(); } else { // 工作没有完成则继续加班 if (work.getHour() < 21) { System.out.println("当前时间" + work.getHour() + "点,又开始加班,疲累之极,**加班。"); } else { // 到点睡觉 work.setCurrentState(new SleepingState()); work.workTime(); } } } }
睡觉类:
/** * @author Shier * CreateTime 2023/5/7 12:15 * 睡觉状态 */ public class SleepingState extends State { @Override public void workTime(Work work) { System.out.println("当前时间" + work.getHour() + "点,睡觉时间到了,躺床就睡着。"); } }
客户端同上
最终的结果也同上
虽然结果相同,但是我们的程序变得更加灵活,比如公司要求在20点之前必须离开公司, 此时我们就要新增一个“强制下班类”,并改动一下 “晚间工作状态”类的判断就可以 了。而这是不影响其他状态的代码的。实现加班类修改如下:
/** * @author Shier * CreateTime 2023/5/7 12:13 * 具体加班类 */ public class EveingState extends State { @Override public void workTime(Work work) { if (work.getWorkFinished()) { // 工作完成,下班 work.setCurrentState(new RestState()); work.workTime(); } else { // 工作没有完成则继续加班 work.setCurrentState(new ForcedLiveWork()); work.workTime(); } } }
强制下班类:
/** * @author Shier * CreateTime 2023/5/7 12:27 * 强制下班 */ public class ForcedLiveWork extends State { @Override public void workTime(Work work) { if (work.getHour() < 20) { System.out.println("当前时间" + work.getHour() + "点,公司规定,此刻必须要离开公司了。"); } else { // 到点睡觉 work.setCurrentState(new SleepingState()); work.workTime(); } } }
实现起来并不困难,只需增加一个类,再对去修改判断条件,这样就不会影响到其他状态的代码
3、状态模式总结
状态模式的优点包括:
将状态转换和行为隔离开来,使得状态变化时只需要改变状态类对象即可,无需修改Context类,从而保证了系统的灵活性、可扩展性和可维护性。
在状态模式中,每个状态都被封装在一个类中,增加新的状态类很方便,符合开闭原则。
通过引入抽象状态类和抽象环境类,可以很好地解决代码的耦合问题。状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖
状态模式使得状态转换显式化了,独立于具体的状态类之外,更符合面向对象的设计思想。
对于状态机的实现,状态模式提供了一种设计思路和方法。
将特定的状态相关的行为都放入一个对象中,由于所有与 状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可 以很容易地增加新的状态和转换。(消除大量的条件判断语句)
状态模式的缺点包括:
由于引入了多个子类,因此在一定程度上增加了系统的复杂度,使得系统抽象层次增加,设计难度加大。
如果状态改变很频繁,则会导致系统中类的数量增加,从而增加系统的维护难度。
如果状态比较多,且状态之间的转换比较复杂,容易造成代码的混乱和不易维护。
状态模式适用场景:
当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态 改变它的行为时,可以考虑使用状态模式。
当对象的行为随着对象内部状态的变化而发生变化时,可以考虑使用状态模式。例如,电视机有开机、关机、切换频道等状态,根据不同的状态做出相应的响应,使用状态模式可以方便地实现。
当系统中存在多种状态且状态之间存在转换关系时,可以考虑使用状态模式。例如,一个产品订单在待支付、已支付、已取消等多个状态之间转换,可以使用状态模式来实现。
当需要对状态进行动态修改时,可以考虑使用状态模式。例如,在游戏中,玩家角色根据当前的状态有不同的技能和装备,而这些状态是可以通过游戏中获得的物品进行修改的,这时候就可以使用状态模式。
当状态转换规则比较复杂或需要进行扩展时,可以考虑使用状态模式。状态模式将状态转换规则封装在具体状态类中,可以方便地对状态转换规则进行修改和扩展。
当希望避免使用大量的if else语句,提高代码可读性和可维护性时,可以考虑使用状态模式。状态模式使得代码易于扩展和修改,并且降低了代码的耦合度,更符合面向对象的设计原则。
状态模式适用场景:
当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态 改变它的行为时,可以考虑使用状态模式。
当对象的行为随着对象内部状态的变化而发生变化时,可以考虑使用状态模式。例如,电视机有开机、关机、切换频道等状态,根据不同的状态做出相应的响应,使用状态模式可以方便地实现。
当系统中存在多种状态且状态之间存在转换关系时,可以考虑使用状态模式。例如,一个产品订单在待支付、已支付、已取消等多个状态之间转换,可以使用状态模式来实现。
当需要对状态进行动态修改时,可以考虑使用状态模式。例如,在游戏中,玩家角色根据当前的状态有不同的技能和装备,而这些状态是可以通过游戏中获得的物品进行修改的,这时候就可以使用状态模式。
当状态转换规则比较复杂或需要进行扩展时,可以考虑使用状态模式。状态模式将状态转换规则封装在具体状态类中,可以方便地对状态转换规则进行修改和扩展。
当希望避免使用大量的if else语句,提高代码可读性和可维护性时,可以考虑使用状态模式。状态模式使得代码易于扩展和修改,并且降低了代码的耦合度,更符合面向对象的设计原则。