状态模式 State 行为型 设计模式(二十四)
状态模式 State
人有喜怒哀乐,海绵宝宝也会有不同的时候,也会有不同的心情~
问题:上图中,如果跟海绵宝宝开玩笑,那种情况最可能被打?
看下面一个示例,演示了java中的多态特性
类A有方法action()
类B继承了A 覆盖了方法action()
类C继承了A 覆盖了方法action()
状态的具体行为,状态切换的时机等等都可以根据实际情况灵活处理,尤其是何时切换状态,谁来负责切换状态。 示例中,在环境类Context中创建了静态的状态对象,你可以根据实际情况动态的创建对象。 如果状态频繁变化,那么事先创建所有状态更合适,如果状态设置后就很少变动,动态创建的形式或许更好。 状态也可以持有环境类的引用,可以获得更多的便利性。
package state.example; public class A { void action() { System.out.println("A......."); } public static void main(String[] args) { A a = new A(); A b = new B(); A c = new C(); a.action(); b.action(); c.action(); } } class B extends A { @Override void action() { System.out.println("B......."); } class C extends A { @Override void action() { System.out.println("C......."); } }类型都是A,但是却因为内部的具体的子类类型不一样,结果不一样 海绵宝宝在不同心情状态下,对同一件事情的处理态度可能是不同的,生气的时候跟他开玩笑很可能会被打。 同样的类型A,由于具体类型状态的不同,给出的响应是不同的。 这就是状态,很多事物都有不同的状态,不同的状态可能会有不同的行为,而且,状态间是可以切换的。
意图
允许一个对象在其内部状态改变时改变他的行为。对象看起来似乎修改了他的类。 别名:状态对象(Objects for States) 其实就是拥有状态属性,在不同的状态下,呈现出不同的行为,并且可以灵活的切换状态。 状态:一个对象的行为取决于内部一个或者多个动态变化的属性,这样的属性就叫做状态。 有状态的对象stateful:这样的对象就叫做有状态的对象。结构
不考虑Java语言的多态,如何达到“多态”的效果? 一种很自然的解决方案就是条件分支或者选择语句,比如int state = 0; if(0 == state){ ??????????????//... }else if(1 == state){ ??????????????//... }else if(2 == state){ ??????????????//... }else{ ??????????????//... }int类型的变量state 就是“有状态的对象”,state的具体的值就是“状态”。 状态的切换依赖state变量的赋值,不同行为的呈现借助于条件分支。 显然,如果业务逻辑复杂,将会有繁琐复杂的分支判断 更有甚者,如果有多个状态共同决定行为,那么岂不是多个状态的复杂组合了? 而且如果新增加状态,那么就需要修改代码,添加一个新的 else if(),扩展性不好,不符合开闭原则。 我们更希望的是能够达到“多态”的那种调用效果,方法调用与具体的行为解耦。 调用者不需要关注具体的类型,在方法执行时,会具有真实类型的行为。 相当于变形为:
int state = 0; state.action();看起来,看起来好像,看起来好像action()方法封装了类似下面的判断逻辑(只是看起来,其实他只有他自己状态下的行为,不需要判断)
if(0 == state){ //... }else if(1 == state){ //... }else if(2 == state){ //... }else{ //... }这不就是上面的多态示例么,如果A表示抽象的状态,B和C表示具体的某种状态 如果所有使用状态的地方,都使用静态类型A,就可以根据实际类型达到多态的效果了。 所以说 状态模式的根本在于借助于OOP的多态机制,描述状态,就可以达到不同行为的效果。 有了不同类型的状态之后,还可以进一步通过一个中间类对他们进行管理,进而实现灵活的状态切换。 所以一种常用的状态模式结果如下 抽象状态角色State 接口或者抽象类,定义状态的抽象含义,并且给出状态的行为接口,这个接口被外界通过环境类调用 可以持有Context的引用,通过Context完成状态切换 具体状态ConcreteState 实现了抽象状态的描述,给出自己的行为,每一个子类型都表示一种具体的状态 环境类角色Context 又叫做上下文,通过环境类Context对状态进行管理 一般做法是将多种状态定义为他的静态属性,环境类中维护一个State,这是当前状态,经常提供切换状态的方法
代码示例
抽象的State,定义状态行为package state; public interface State { void handle(); }具体的状态1
package state; public class ConcreateState1 implements State { @Override public void handle() { System.out.println("state 1 do sth"); } }具体的状态2
package state; public class ConcreateState2 implements State { @Override public void handle() { System.out.println("state 2 do sth"); } }环境类,内部封装了两个状态 state为当前状态,初始时设置为状态1了 通过changeState方法进行切换 action()方法为封装调用state的handle方法
package state; public class Context { private final State STATE1 = new ConcreateState1(); private final State STATE2 = new ConcreateState2(); private State state = STATE1; public void action() { state.handle(); } public void changeState(int stateValue) { if (1 == stateValue) { state = STATE1; } else if (2 == stateValue) { state = STATE2; } } }上面的示例代码中,初始状态为状态1 ,action调用状态1,打印 state 1 do sth 状态切换后,调用状态2 状态模式的核心在于引入抽象状态角色State,借助于多态,实现不同的行为, 然后借助于环境类Context实现State的管理,以灵活切换状态。
状态的具体行为,状态切换的时机等等都可以根据实际情况灵活处理,尤其是何时切换状态,谁来负责切换状态。 示例中,在环境类Context中创建了静态的状态对象,你可以根据实际情况动态的创建对象。 如果状态频繁变化,那么事先创建所有状态更合适,如果状态设置后就很少变动,动态创建的形式或许更好。 状态也可以持有环境类的引用,可以获得更多的便利性。
另外的示例
如果仅仅是状态的提取,简化复杂的状态判断逻辑,借助于State角色就可以完成 如果需要状态的切换维护,或者要求状态的顺序等,总之对State的访问需要增加更多的业务逻辑时,那么就可以借助于Context对State进行管理 通过Context封装维护当前状态,也就是Context提供代理方法,代理对当前状态State的直接访问 这样就可以通过Context增加状态切换,顺序限制等更多的处理逻辑 比如下面逻辑,假设状态1,2,3,4,5,通过下面的逻辑,就可以控制状态的切换顺序,当前状态推导出下一个状态void click(){
currentState.handle();
if(state == 1){
changeState(2);
}else if(state == 2){
changeState(3);
}
//.....
}
package state.order; public interface State { void handle(); }
package state.order; public class ConcreateState1 implements State { @Override public void handle() { System.out.println("state 1 do sth"); } }
package state.order; public class ConcreateState2 implements State { @Override public void handle() { System.out.println("state 2 do sth"); } }
package state.order; public class ConcreateState3 implements State { @Override public void handle() { System.out.println("state 3 do sth"); } }环境Context包括三种状态,初始时当前状态为状态1 每次方法调用,都会按照顺序切换状态
package state.order; public class Context { private final State STATE1 = new ConcreateState1(); private final State STATE2 = new ConcreateState2(); private final State STATE3 = new ConcreateState3(); private State state = STATE1; public void action() { state.handle(); if (state == STATE1) { setState(STATE2); } else if (state == STATE2) { setState(STATE3); } else if (state == STATE3) { setState(STATE1); } } void setState(State state) { this.state = state; } }从上面的结果可以看得出来,不管你调用多少次方法,他都会完成两个任务,当前状态处理,然后切换状态 所以说,状态的切换时机,场景,不同的方式将会有很多种不同的变化形式。