前面有总结——策略模式,以前早就以为策略和状态设计模式有一些类似……参考:继承、组合和接口用法——策略模式复习总结 ,该模式其实也很经常使用,我常常把它和策略模式结合着用,来减小大量的 if-else 代码片断。html
策略模式是对象的行为模式,其实就是对一系列级别平等的算法的封装,它不关心算法实现,让客户端去动态的依靠 “环境” 类去选择须要的算法,由于他们能互相替换,能够说策略模式使能一系列算法能够平滑的切换。那么状态(State)模式,也是对象的行为设计模式的一种。java
官方教科书是这样定义的:正则表达式
状态模式容许经过改变对象的内部状态而改变对象的行为,这个对象表现得就好像修改了它的类同样。算法
呵呵,记得早以前学设计模式,学到状态模式的概念,这么一看,这是解释的鸡毛啊…… 先直接看个小例子,顺着它的定义来推演:编程
/** * Person*/ public class Person { /** * 这我的有一个闹表,靠它的时间变化(状态修改)来决定什么时候作什么(改变行为) */ private int hour; public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } /** * 人的一个行为 * * 状态模式容许经过改变一个对象的内部状态,来改变对象的行为,就像修改了对象的类同样! */ public void doSth() { // 那么我就模拟修改类的对象的内部状态 if (this.hour == 7) { System.out.println("起床啦!"); } else if (this.hour == 11) { System.out.println("吃中午餐了!"); } else if (this.hour == 19) { System.out.println("吃晚饭了!"); } else if (this.hour == 22) { System.out.println("睡觉咯!"); } else { System.out.println("学习呢!"); } } } public class MainState { public static void main(String[] args) { Person person = new Person(); person.setHour(7); person.doSth();// 起床啦! person.setHour(11); person.doSth();// 吃中午餐了! person.setHour(19); person.doSth();// 吃晚饭了! person.setHour(22); person.doSth();// 睡觉咯! person.setHour(10); person.doSth();// 学习呢! } }
这个例子,确实就是状态模式描述的场景,有一个Person类,它有一个时间对象——闹表,经过时间的变化(修改对象的内部状态)来改变对象的行为(人的一些睡觉,学习的行为),这个对象表现的就比如修改了它的类同样。设计模式
可是,这个例子并无使用状态模式来实现,Person类设计的很low,由于有大量的 if-else 不易维护……那么这个场景下,应该使用本文提到的状态模式。尝试实现:数组
public abstract class State { /** * 抽象状态(接口)角色,封装了和环境类(Person类)的对象的状态(闹表时间的变化)相关的行为 */ public abstract void doSth(); } public class GetUp extends State { /** * 各个具体的状态角色,实现状态类, */ @Override public void doSth() { System.out.println("起床啦!"); } } public class HaveDinner extends State { @Override public void doSth() { System.out.println("吃晚饭了!"); } } public class HaveLunch extends State { @Override public void doSth() { System.out.println("吃中午餐了!"); } } public class Sleep extends State { @Override public void doSth() { System.out.println("睡觉咯!"); } } public class Study extends State { @Override public void doSth() { System.out.println("学习呢!"); } } public class Person { /** * 这我的有一个闹表,靠它的时间变化(状态修改)来决定什么时候作什么(改变行为) */ private int hour; private State state; public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } /** * 人(环境类)的个行为 * * 状态模式容许经过改变一个对象的内部状态,来改变对象的行为,就像修改了对象的类同样! */ public void doSth() { if (this.hour == 7) { state = new GetUp(); state.doSth(); } else if (this.hour == 11) { state = new HaveLunch(); state.doSth(); } else if (this.hour == 19) { state = new HaveDinner(); state.doSth(); } else if (this.hour == 22) { state = new Sleep(); state.doSth(); } else { state = new Study(); state.doSth(); } } } public class MainStateA { public static void main(String[] args) { Person person = new Person(); person.setHour(7); person.doSth();// 起床啦! person.setHour(11); person.doSth();// 吃中午餐了! person.setHour(19); person.doSth();// 吃晚饭了! person.setHour(22); person.doSth();// 睡觉咯! person.setHour(10); person.doSth();// 学习呢! } }
确实有了变化,把以前的Person类对象的内部状态的改变对应的Person行为的变化作了封装,变成了类来表示,可是并无什么实质上的改变。安全
Person类依然有大量不易维护的if-else语句,而状态模式的使用目的就是控制一个对象状态转换的条件表达式过于复杂时的状况——把状态的判断逻辑转译到表现不一样状态的一系列类当中,能够把复杂的判断逻辑简化。网络
上一版本没有把对应状态的判断逻辑同时转移,仍是留在了环境类(Person类)里……继续优化:数据结构
public abstract class State { /** * 抽象状态(接口)角色,封装了和环境类(Person类)的对象的状态(闹表时间的变化)相关的行为 */ public abstract void doSth(PersonB personB); } public class GetUp extends State { /** * 各个具体的状态角色,实现状态类, */ @Override public void doSth(PersonB personB) { if (personB.getHour() == 7) { System.out.println("起床啦!"); } else { // 转移状态 personB.setState(new HaveLunch()); // 必需要调用行为 personB.doSth(); } } } public class HaveDinner extends State { @Override public void doSth(PersonB personB) { if (personB.getHour() == 19) { System.out.println("吃晚饭了!"); } else { personB.setState(new Sleep()); personB.doSth(); } } } public class HaveLunch extends State { @Override public void doSth(PersonB personB) { if (personB.getHour() == 11) { System.out.println("吃中午餐了!"); } else { personB.setState(new HaveDinner()); personB.doSth(); } } } public class Sleep extends State { @Override public void doSth(PersonB personB) { if (personB.getHour() == 22) { System.out.println("睡觉咯!"); } else { personB.setState(new Study()); personB.doSth(); } } } public class Study extends State { @Override public void doSth(PersonB personB) { // 如此,不再须要向下传递状态了! System.out.println(personB.getHour() + "点,正学习呢!"); } }
把以前放到环境类里的对当前对象状态的逻辑判断(条件表达式……),随着不一样的状态放到了对应的状态类里,且同时让状态动态的迁移——这里又有责任链模式的影子。并且继承的抽象状态类的行为方法里加上了环境类的对象做为参数。以起床状态为例:
public void doSth(PersonB personB) { if (personB.getHour() == 7) { System.out.println("起床啦!"); } else { // 转移状态 personB.setState(new HaveLunch()); // 必需要调用行为 personB.doSth(); } }
当getup状态类的if判断不知足时,就转移状态到下一个——set 一个新状态去覆盖旧状态……同时记得调用下一个状态的行为(执行doSth方法)。
PS:这里很是像责任链(职责链)模式。最后一个状态——学习类,没有转移的其余状态了,那么就不须要转移,直接设置为终结状态(在责任链模式里是依靠判断get到的连接对象是否为null来判断职责链条的终点的)。
以下:
public class Study extends State { @Override public void doSth(PersonB personB) { // 如此,最后一个状态(或者说表明其余的状态)不再须要向下传递状态了! System.out.println(personB.getHour() + "点,正学习呢!"); } }
再看环境类,和客户端(客户端代码不须要变化)
public class PersonB { /** * 这我的有一个闹表,靠它的时间变化(状态修改)来决定什么时候作什么(改变行为) */ private int hour; private State state; public State getState() { return state; } public void setState(State state) { this.state = state; } public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } public PersonB() { // 在构造器里初始化状态,从早晨起床开始 this.state = new GetUp(); } /** * 人(环境类)的个行为 * * 状态模式容许经过改变一个对象的内部状态,来改变对象的行为,就像修改了对象的类同样! */ public void doSth() { // 传入的是PersonB的对象 state.doSth(this); } } public class MainStateB { public static void main(String[] args) { PersonB personB = new PersonB(); personB.setHour(7); personB.doSth(); personB.setHour(11); personB.doSth(); personB.setHour(19); personB.doSth(); personB.setHour(22); personB.doSth(); personB.setHour(10); personB.doSth(); } }
打印:
起床啦!
吃中午餐了!
吃晚饭了!
睡觉咯!
10点,正学习呢!
貌似 ok。。。睡觉到次日,早晨又该起床……给客户端顺序增了一个7点的状态
public class MainStateB { public static void main(String[] args) { PersonB personB = new PersonB(); personB.setHour(7); personB.doSth(); personB.setHour(11); personB.doSth(); personB.setHour(19); personB.doSth(); personB.setHour(22); personB.doSth(); personB.setHour(10); personB.doSth(); personB.setHour(7); personB.doSth();// 有问题 } }
发现打印以下:
起床啦!
吃中午餐了!
吃晚饭了!
睡觉咯!
10点,正学习呢!
7点,正学习呢!
分析前面的问题:7点应该是“起床啦!”。
这说明以前的状态模式的实现代码有问题,问题出在环境类(Person类)的初始化上,客户端 new 了一我的,则person的构造器自动初始化状态为getup,把对象的内部状态修改,会去寻找对应的状态类,找不到就迁移到下一个状态,它的状态迁移是单向不可逆的……如图:
优化以下,只需修改环境类——Person,每次搜索,都要重置状态,即从getup 开始搜索,核心思想是每次对象内部状态改变以后,都把状态迁移复位一下。记住是搜索一次以后复位。
public class PersonB { /** * 这我的有一个闹表,靠它的时间变化(状态修改)来决定什么时候作什么(改变行为) */ private int hour; private State state; public State getState() { return state; } public void setState(State state) { this.state = state; } public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } public PersonB() { // 在构造器里初始化状态,从早晨起床开始 this.state = new GetUp(); } /** * 人(环境类)的个行为 * * 状态模式容许经过改变一个对象的内部状态,来改变对象的行为,就像修改了对象的类同样! */ public void doSth() { // 传入的是PersonB的对象 state.doSth(this); // 每次都从头开始搜索状态类 this.state = new GetUp(); } }
这样就ok了。
小结:状态模式隐含着责任链模式的部分思想,而UML类图的设计上和策略模式很是类似,下面继续分析。
前面的例子确实是状态模式,可是每次复位状态的时候,还有搜索状态的时候,都要new 一个状态对象,太浪费内存了,故每每实际工程里,都会结合单例模式,把每一个状态类都搞成单例,若是实在搞不成,就要从新思考设计了。
参考:最简单的设计模式——单例模式的演进和推荐写法(Java 版)
Context:用户对象,拥有(聚合)一个State类型的成员,以标识对象的当前状态,就是Person类
State:接口或基类,封装与Context的特定状态相关的行为;
ConcreteState:接口实现类或子类,实现了一个与Context某个状态相关的行为。
是否是和策略模式的类图很像很像:一样的一个抽象类(接口),包含一个行为,和N个具体实现的类,外加一个环境类(聚合了接口引用)……
两个模式的实现类图虽然一致,可是实现目的不同。
首先,策略模式是一个接口的应用案例,一个很重要的设计模式,简单易用,通常用于单个算法的替换,客户端事先必须知道全部的可替换策略,由客户端去指定环境类须要哪一个策略,注意一般都只有一个最恰当的策略(算法)被选择。其余策略是同级的,可互相动态的在运行中替换原有策略。
而状态模式的每一个状态类须要包含环境类(Context)中的全部方法的具体实现——条件语句。经过把行为和行为对应的逻辑包装到状态类里,在环境类里消除大量的逻辑判断,而不一样状态的切换由继承(实现)State的状态子类去实现,当发现修改的当前对象的状态不是本身这个状态所对应的参数,则各个状态子类本身给Context类切换状态(有职责链模式思想),且客户端不直接和状态类交互,客户端不须要了解状态。
这点和策略模式不同,策略模式是直接依赖注入到Context类的参数进行选择策略,不存在切换状态的操做,客户端须要了解策略。
联系;状态模式和策略模式都是为具备多种可能情形设计的模式,把不一样的处理情形抽象为一个相同的接口(抽象类),符合对开闭原则,且策略模式更具备通常性,在实践中,能够用策略模式来封装几乎任何类型的规则,只要在分析过程当中听到须要在不一样实践应用不一样的业务规则,就能够考虑使用策略模式处理,在这点上策略模式是包含状态模式的功能的
状态模式主要解决的是:控制一个对象内部的状态转换的条件表达式过于复杂时的状况,且客户端调用以前不须要了解具体状态。它把状态的判断逻辑转到表现不一样状态的一系列类当中,能够把复杂的判断逻辑简化。维持开闭原则,方便维护,还有重要一点下面会总结,状态模式是让各个状态对象本身知道其下一个处理的对象是谁,即在状态子类编译时在代码上就设定好了。
优势,前面说了不少了……
一、状态模式使得代码中复杂而庸长的逻辑判断语句问题获得了解决,并且状态角色将具体的状态和他对应的行为及其逻辑判断封装了起来,这使得增长一种新的状态显得十分简单。
二、把容易出错的if-else语句在环境类 or 客户端中消除,方便维护
三、每个状态类都符合“开闭”原则——对状态的修改关闭,对客户端的扩展开放,能够随时增长新的Person的状态,或者删除。
四、State类在只有行为须要抽象时,就用接口,有其余共同功能能够用抽象类,这点和其余一些(策略)模式相似。
缺点,我的认为微不足道
使用状态模式时,每一个状态对应一个具体的状态类,使结构分散,类的数量变得不少,使得程序结构变得稍显复杂,阅读代码时相对以前比较困难,不过对于优秀的研发人员来讲,应该是微不足道的。
由于想要获取弹性,就必须付出代价,除非程序是一次性的,用完就丢掉……若是不是,那么假设有一个系统,某个功能须要不少状态,若是不使用状态模式优化,那么在环境类(客户端类)里会有大量的整块整块的条件判断语句。这才尼玛是真正的变得很差理解,lz我是实习生的时候,在xx公司(匿名了)就见过有人写这样的代码,一个方法或者一个类,动不动几千行代码……重要的是里面一大块一大块的if-else……还倍感优越。。。看,我写的快不快。。。
状态模式偏偏是看着类多了,实际上是让状态变的清晰,让客户端和环境类都彼此干净,更加方便理解和维护。
有时业务不是很复杂,参数校验不是不少的时候,固然可使用if或者if-else逻辑块或者switch-case块来进行编码,可是一旦扩展了程序,增长了业务,或者开始就有不少不少的逻辑判断分支,这并非一件好事,它首先不知足OCP——开闭原则,一旦须要修改判断方法或者类,那么牵一发动全身,经常整个逻辑块都须要大改,责任没有分解,对象内部状态的改变和对应逻辑都杂糅在了一块儿,也不符合单一职责原则,偏偏此时,我但愿分解整个判断过程,分离职责,把状态的判断逻辑转移到表示不一样状态的一系列类当中,把复杂的判断逻辑简化,这就是刚刚说的状态模式。状态模式把当前类对象的内部的各类状态转移逻辑分布到State抽象类的子类中,这样减小了各个逻辑间的依赖,客户端也不须要实现了解各个状态。
不过,综上总结,我发现,状态模式是让各个状态对象本身知道其下一个处理的对象是谁!即在编译时在代码上就设定好了!好比以前例子的状态子类:
public class GetUp extends State { /** * 各个具体的状态角色,实现状态类, */ @Override public void doSth(PersonB personB) { if (personB.getHour() == 7) { System.out.println("起床啦!"); } else { // 转移状态,明确知道 要转移到哪一个 已有 的状态! personB.setState(new HaveLunch()); // 必需要调用对应状态的行为 personB.doSth(); } } }
若是有一种复杂逻辑判断,好比公司考勤系统处理员工请假的流程,不一样级别,类型,部门等的员工的请假流程是不同的,咱们没法知道员工该状态的下一个状态是什么。。。
好比老王是临时工,请假只须要直接领导批准,老李是正式工,请假须要先让直接领导审批,再交给主管批准,老张是安所有门的员工,请假须要的流程更复杂……或者哪天系统变化升级,请假制度修改了……换句话说就是请假系统里请假相关的各个对象并不指定(也不知道)其下一个处理的对象究竟是谁,只有在客户端才设定。这怎么办?这就须要责任链设计模式解决,二者类图不同,具体解耦责任,转移对象的流程略微的不同,可是总的目标一致:参考:大量逻辑判断优化的思路——责任链模式复习总结及其和状态模式对比。
大致上看,责任链模式要比状态模式灵活,虽然职责链模式灵活,可是遵循够用原则,好比前面的状态模式的例子:Person类的闹表记录一天的状态及其对应的行为,各个状态(判断逻辑)明确知道其下一个状态(处理对象)是谁,在内部编码时就肯定了,状态模式就ok了,用责任链就显得很呵呵,适合就好。
还有简单情景下,可使用三元运算符 condition ? : 代替简单的if-else语句,或者数组这种随机存储乃至查询性能很好的数据结构替换switch-case。
可是我想的是设计模式的阴暗面,不要为了用设计模式而用设计模式,对于switch-case语句块,也不要过分优化,数量不是很大时,switch的性能也不差,不必优化什么,想起来《Java编程思想》做者和《重构》一书做者都说过的:
等到无可奈何必需要这么作的时候,再想优化,不要陷入优化和设计模式的陷阱。
常见的就是java.util.Iterator
先看教科书的具体定义:
(Finite-state machine, FSM),又称有限状态自动机,是表示有限个状态以及在这些状态之间的转移和动做等行为的数学模型。
FSM是一种算法思想,简单而言,有限状态机由一组状态、一个初始状态、输入和根据输入及现有状态转换为下一个状态的转换函数组成。
本文总结的State模式(状态模式)其实本质就是一种面向对象的状态机思想,能够适应很是复杂的状态管理。
它反映从系统开始到如今时刻输入的变化,以及各个状态之间转移的指示变动,并且必须使用能知足状态转移的条件来描述,状态机里的动做是在给定时刻要进行的活动描述,状态机里的状态存储的是有关于过去的信息,它有多种类型的动做:
说了那么多,它到底能干吗的呢,其实不论编程仍是生活里,状态机无时不在。我知道,编程是对现实的抽象,状态机也是,当业务逻辑里有大量逻辑判断须要各类来回的转换状态时,有限状态机就有用了,本质上其是用查表法把处理逻辑独立到表中:
能够用通用的代码去处理任意复杂的状态转换,扩展开来,任何复杂状态逻辑的处理均可以好比: