人物:大鸟,小菜
事件:小菜向大鸟抱怨经常加班,并向大鸟阐述了一天上班各时间段自己的工作状态,然后大鸟借这个事给小菜讲解了状态模式
状态模式:
1.小菜用代码描述自己一天不同时间段的状态
2.改进代码,面向对象
3.了解什么是状态模式
4.进一步改进代码,使用状态模式
5.了解状态模式和策略模式的区别
用代码描述一天状态
@Slf4j public class WriteProgram { static int hour = 0; static boolean workFinished = false; public static void writeProgram() { if (hour < 12) { log.info("当前时间:{}点,上午工作,精神百倍", hour); } else if (hour < 13) { log.info("当前时间:{}点,饿了,午饭:犯困,午休", hour); } else if (hour < 17) { log.info("当前时间:{}点,下午状态还不错,继续努力", hour); } else { if (workFinished) { log.info("当前时间:{}点 下班回家了", hour); } else { log.info("当前时间:{}点 不行了,睡着了。", hour); } } } public static void main(String[] args) { hour = 9; writeProgram(); hour = 10; writeProgram(); hour = 12; writeProgram(); hour = 13; writeProgram(); hour = 14; writeProgram(); hour = 17; workFinished = true; writeProgram(); hour = 19; writeProgram(); hour = 22; writeProgram(); } }
大鸟:你这不还是面向过程开发么,你面向对象至少应该有个工作类吧
工作状态分类版
抽离出的工作类:
@Slf4j @Data public class Work { private int hour; private boolean finish = false; public boolean taskFinished; public void writeProgram() { if (hour < 12) { log.info("当前时间:{}点,上午工作,精神百倍", hour); } else if (hour < 13) { log.info("当前时间:{}点,饿了,午饭:犯困,午休", hour); } else if (hour < 17) { log.info("当前时间:{}点,下午状态还不错,继续努力", hour); } else { if (finish) { log.info("当前时间:{}点 下班回家了", hour); } else { log.info("当前时间:{}点 不行了,睡着了。", hour); } } } }
客户端代码:
@Slf4j public class WriteProgram { public static void main(String[] args) { Work emergencyProjects = new Work(); emergencyProjects.setHour(9); emergencyProjects.writeProgram(); emergencyProjects.setHour(10); emergencyProjects.writeProgram(); emergencyProjects.setHour(12); emergencyProjects.writeProgram(); emergencyProjects.setHour(13); emergencyProjects.writeProgram(); emergencyProjects.setHour(14); emergencyProjects.writeProgram(); emergencyProjects.setHour(17); emergencyProjects.setTaskFinished(false); emergencyProjects.writeProgram(); emergencyProjects.setHour(19); emergencyProjects.writeProgram(); emergencyProjects.setHour(22); emergencyProjects.writeProgram(); } }
结果输出:
当前时间:9点,上午工作,精神百倍
当前时间:10点,上午工作,精神百倍
当前时间:12点,饿了,午饭:犯困,午休
当前时间:13点,下午状态还不错,继续努力
当前时间:14点,下午状态还不错,继续努力
当前时间:17点 不行了,睡着了。
当前时间:19点 不行了,睡着了。
当前时间:22点 不行了,睡着了。
大鸟:这种设计有一个问题就是,方法过长导致了坏味道,面向对象设计其实就是为了责任分解,work类违背了单一职责原则,而且万一有新加判断,要在if...else...加逻辑,破坏了开放封闭原则,用状态模式可以更好的设计这个事件
状态模式
1.概念:当一个对象的内在状态改变时,允许改变其行为,这个对象像是改变了其类
2.主要解决什么问题:状态模式主要解决的是,当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移不同状态的一系列类中,可以把复杂的判断逻辑简化。当然如果这个判断很简单,就没有必要状态模式了
3.代码结构图:
首先实现State类,是抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为
public abstract class State { public abstract void handle(Context context); }
ConcreteStateA,ConcreteStateB都是具体的状态,每一个子类实现一个与Context的一个状态相关的行为
public class ConcreteStateA extends State { @Override public void handle(Context context) { context.setState(new ConcreteStateB()); } }
public class ConcreteStateB extends State{ @Override public void handle(Context context) { context.setState(new ConcreteStateA()); } }
Context类,维护每一个ContextState子类的实例,这个实例定义当前的状态
@Data public class Context { private State state; public Context(State state) { this.state = state; } public void request() { state.handle(this); } }
客户端代码:
@Slf4j public class WriteProgram { public static void main(String[] args) { Context c = new Context(new ConcreteStateA()); c.request(); c.request(); c.request(); c.request(); } }
状态模式的好处与用处:
1.好处:将与特定状态相关的行为局部化,并且将不同状态的行为分割开来(消除了庞大的条件分支语句)
2.什么时候用:当一个对象的行为取决于它的状态,并且它必须在运行时要根据状态改变它的行为时,就可以用状态模式了
工作状态--状态模式版
抽象状态类:
public abstract class State { public abstract void writeProgram(Work w); }
各个时间段的状态类:
上午状态工作类:
@Slf4j public class ForenoonState extends State { @Override public void writeProgram(Work w) { if (w.getHour() < 12) { log.info("当前时间:{}点,上午工作,精神百倍", w.getHour()); } else { w.setState(new AfternoonState()); w.writeProgram(); } } }
中午工作状态类:
@Slf4j
public class NoonState extends State {
@Override
public void writeProgram(Work w) {
if (w.getHour() < 13) {
log.info("当前时间:{}点 饿了,午饭:犯困,午休。", w.getHour());
} else {
w.setState(new AfternoonState());
w.writeProgram();
}
}
}
下午状态工作类:
@Slf4j public class AfternoonState extends State { @Override public void writeProgram(Work w) { if (w.getHour() < 17) { log.info("当前时间:{}点,下午状态不错,继续努力", w.getHour()); } else { w.setState(new EveningState()); w.writeProgram(); } } }
傍晚状态工作类:
@Slf4j public class EveningState extends State { @Override public void writeProgram(Work w) { if (w.taskFinished) { w.setState(new RestState()); w.writeProgram(); } else { if (w.getHour() < 21) { log.info("当前时间:{}点 加班哦,疲累之极", w.getHour()); } else { w.setState(new SleepingState()); w.writeProgram(); } } } }
睡眠状态工作类:
@Slf4j public class SleepingState extends State { @Override public void writeProgram(Work w) { log.info("当前时间:{}点不行了,睡着了", w.getHour()); } }
下班休息状态类:
@Slf4j public class RestState extends State { @Override public void writeProgram(Work w) { log.info("当前时间:{}点下班回家了", w.getHour()); } }
工作类:
public class Work { private int hour; private boolean finish = false; public boolean taskFinished; private State state; public Work() { state = new ForenoonState(); } public void writeProgram() { state.writeProgram(this); } }
这样客户端代码没有改动,程序却更加灵活了
状态模式和策略模式的区别
策略模式:其思想是针对一组算法,将每一种算法都封装到具有共同接口的独立的类中,从而是它们可以相互替换。而且策略模式的客户端必须对所有的策略类相当了解,明确当前场景下各种策略的利弊,权衡在当前场景下应该使用哪种策略,也就是说策略类对客户端是暴露的。
状态模式:允许一个对象在其内部状态改变时改变它的行为。它依赖于其状态的变化时其内部的行为发生变化,将动作委托到代表当前状态的对象,对外表现为类发生了变化。