设计模式(java实现)_状态模式(State)
我总感觉网上写的和我写的不是同一个模式似的…
我这个状态切换的流畅的一批,而且外部调用者完全与其状态的变换隔离。
也完美符合迪米特法则,新增状态原有代码逻辑一丝都不用修改。
就是实现起来略复杂
核心:
- 用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题
结构:
-Context环境类
- 环境类中维护一个State对象,他是定义了当前的状态
-State抽象状态类
-ConcreteState具体状态类
- 每一个类封装了该状态对应的行为
简单点说:
状态模式封装的非常好,状态的变更引起了行为的变更,状态变换放置到了类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换,从外部看起来就好像这个对象对应的类发生了改变一样。
下面是我写的代码,用酒店的房间作为例子。
其房间有三种状态,1:空闲、2:预定、3:已入住,我将以这三种状态的变换来演示状态模式的奥妙
State 接口
package com.skypyb.state.homeState; import com.skypyb.state.Context; /** * state 模式核心父接口,所有状态继承该类 * * @author pyb www.skypyb.com www.yibobo.top */ public abstract class State { //维护一个Context环境,目的是能够让子类操纵,以达到状态动态变换的效果 protected Context context; public void setContext(Context context) { this.context = context; } public abstract void free(); public abstract void booked(); public abstract void checkedin(); }
Context环境类:
package com.skypyb.state; import com.skypyb.state.homeState.BookedState; import com.skypyb.state.homeState.CheckedinState; import com.skypyb.state.homeState.FreeState; import com.skypyb.state.homeState.State; /** * 环境类,表示状态所处的环境,已初始化好所有的状态对象(public final 修饰,暴露给外部但不可变更) * * 外部调用者只需要操纵该类即可完成状态的变更,每当状态变更时其所维护的状态类都会动态改变,其细节对外隐藏 * * @author pyb www.skypyb.com www.yibobo.top */ public class Context { private State state;//所维护的状态 public final BookedState bookedState = new BookedState(); public final CheckedinState checkedinState = new CheckedinState(); public final FreeState freeState = new FreeState(); public State getState() { return state; } public void setState(State state) { this.state = state; this.state.setContext(this);//将自身设置到所维护的 state中 } //下面仨是客户端进行状态改变时的实际会调用的方法 //客户端将完全和具体的状态变更隔离,符合迪米特法则 public void free() { this.state.free(); } public void booked() { this.state.booked(); } public void checkedin() { this.state.checkedin(); } }
说明:
到这就需要说明一下。这个状态和环境是一个你中有我,我中有你的结构。可以看到他们都有一个 Set方法 ,这个方法就是用于动态的变换。会经常用到。
状态中有环境,是因为状态变更时需要操作环境,将环境所维护的状态改变。状态变更时如何操作环境?你将在下面几个子类中看到
环境中有状态,环境为何叫做环境?自然是代表状态所处的环境,客户端进行状态切换时,所面对的就是该环境对象。即调用方法依赖环境而不是状态。
3种房间的状态:
package com.skypyb.state.homeState; /** * 表示房间空闲状态 */ public final class FreeState extends State { //切换到空闲状态时会触发的方法 @Override public void free() { System.out.println("空闲状态~"); } @Override public void booked() {//空闲状态下进行切换到预定操作 super.context.setState(super.context.bookedState); super.context.getState().booked(); } @Override public void checkedin() {//空闲状态下进行切换到入住操作,没预定当然是不能入住的啦 System.out.println("没预定你住你马呢?"); } }
package com.skypyb.state.homeState; /** * 表示房间预订状态 */ public final class BookedState extends State { @Override public void free() {//预定状态下进行切换到空闲操作 System.out.println("退房,行吧"); super.context.setState(super.context.freeState); super.context.free(); } @Override public void booked() {//切换到房间预订状态时会触发的方法 System.out.println("已预订"); } @Override public void checkedin() {//预定状态下进行切换到入住操作 super.context.setState(super.context.checkedinState); super.context.checkedin(); } }
package com.skypyb.state.homeState; /** * 表示房间入住状态 */ public final class CheckedinState extends State { @Override public void free() {//入住状态下进行切换到空闲操作 System.out.println("退房,行吧"); super.context.setState(super.context.freeState); super.context.getState().free(); } @Override public void booked() {//预定状态下进行切换到空闲操作,这里我让其不能变更状态 System.out.println("住进去了还改为预定?怕不是傻子"); } @Override public void checkedin() {//切换到入住状态时会执行的方法 System.out.println("已入住"); } }
在状态变更时,会进行两步处理:
1、将环境类 Context 维护的状态改变为切换后的状态
2、触发该状态的方法
假设现在客户端拥有一个状态为【空闲】的环境,那么,在他调用环境的 booked() 方法时,会触发如下现象:
1、环境所维护的状态对象由【空闲】变为【预定】
2、预定对象的 booked() 方法被调用,打印出”已预订”。
此时环境已经为预定状态。
下面看我使用一个客户端演示切换状态:
package com.skypyb.state; public class Client { public static void main(String[] args) { Context context = new Context(); //将状态设置到环境中,然后就可以不管了。 //之后状态随意切换,至于切换了状态会发生啥?关我这个调用者屁事 context.setState(context.freeState);//一开始我设置为空闲状态 //下面是状态的切换,注释标明了其切换状态是否成功 context.booked();//试图转换为预定 true context.free();//试图转换为空闲 true context.checkedin();//试图转换为入住 false context.booked();//试图转换为预定 true context.checkedin();//试图转换为入住 true context.free();//试图转换为空闲 true } }
System.out:
已预订
退房,行吧
空闲状态~
没预定你住你马呢?
已预订
已入住
退房,行吧
空闲状态~
看看,是不是这个客户端只用管切换状态就对了,其余的都与其无关?
观察我写的代码,可以发现状态模式可扩展性极强,若想新增一个状态,源代码逻辑一点都不需要变动。只需要在新增一个状态类并且在接口和环境中新增对应的方法和属性即可。真正的对修改关闭对扩展开放。
这个模式实现起来还是比较抽象的。若是想在实际项目中将其优势完美发挥,估计得需要一点编码内力才行。我也不确定我能否将其完美运用。估计机会也不多。
不过,状态模式确实使我有种:原来代码还能这么玩? 的感觉,真的是太灵性了。