状态模式
状态模式(State):当一个对象的内在状态g改变时允许改变其行为,这个对象看起来像是改变了其类。
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列l类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简单,那就没必要用‘状态模式’。--《大话设计模式》
Context: 环境类。可以包括一些内部状态。
State: 抽象状态类。State定义了一个所有具体状态的共同接口,任何状态都实现这个相同的接口,这样一来,状态之间就可以互相转换了。
ConcreteState: 具体状态类。具体状态类,用于处理来自Context的请求,每一个ConcreteState都提供了它对自己请求的实现,所以,当Context改变状态时行为也会跟着改变。
接下来,用一个酒店房间的例子介绍状态模式。
当你第一眼看到这个系统的时候你就看出来了这是一个状态图,每个框框都代表了房间的状态,箭头表示房间状态的转换。分析如下:房间有三个状态:空闲、已预订、已入住,状态与状态之间可以根据客户的动作来进行转换。定义每个状态的值。
状态接口
package state; /** * * Title: State * Description: 状态接口 * @author yacong_liu Email:2682505646@qq.com * @date 2019年1月16日 */ public interface State { /** * * <p>Title: bookRoom</p> * <p>Description: 预定房间</p> */ void bookRoom(); /** * * <p>Title: unsubscribeRoom</p> * <p>Description: 退订房间</p> */ void unsubscribeRoom(); /** * * <p>Title: checkInRoom</p> * <p>Description: 入住</p> */ void checkInRoom(); /** * * <p>Title: checkOutRoom</p> * <p>Description: 退房</p> */ void checkOutRoom(); }
房间
package state; /** * * Title: Room Description: 房间 * * @author yacong_liu Email:2682505646@qq.com * @date 2019年1月16日 */ public class Room { /* * @desc 空闲状态 */ State freeState; /* * @desc 预订状态 */ State bookedState; /* * @desc 入住状态 */ State checkInState; State state; public Room() { this.freeState = new FreeState(this); this.bookedState = new BookedState(this); this.checkInState = new CheckInState(this); // 初始状态为空闲状态 this.state = freeState; } /** * * <p> * Title: bookRoom * </p> * <p> * Description: 预订房间 * </p> */ public void bookRoom() { state.bookRoom(); } /** * * <p> * Title: unsubsribeRoom * </p> * <p> * Description: 退订房间 * </p> */ public void unsubsribeRoom() { state.unsubscribeRoom(); } /** * * <p> * Title: checkInRoom * </p> * <p> * Description: 入住 * </p> */ public void checkInRoom() { state.checkInRoom(); } /** * * <p> * Title: checkOutRoom * </p> * <p> * Description: 退房 * </p> */ public void checkOutRoom() { state.checkOutRoom(); } /* * getter setter */ public State getFreeState() { return freeState; } public void setFreeState(State freeState) { this.freeState = freeState; } public State getBookedState() { return bookedState; } public void setBookedState(State bookedState) { this.bookedState = bookedState; } public State getCheckInState() { return checkInState; } public void setCheckInState(State checkInState) { this.checkInState = checkInState; } public State getState() { return state; } public void setState(State state) { this.state = state; } @Override public String toString() { return "该房间的状态是:" + getState().getClass().getName(); } }
空闲状态
package state; /** * * Title: FreeState Description: 空闲状态 (可预定和入住) * * @author yacong_liu Email:2682505646@qq.com * @date 2019年1月16日 */ public class FreeState implements State { Room hotelManager; public FreeState(Room hotelManager) { this.hotelManager = hotelManager; } @Override public void bookRoom() { System.out.println("您已经成功预订了..."); // 房间状态切换为已预定状态 hotelManager.setState(hotelManager.getBookedState()); } @Override public void unsubscribeRoom() { System.out.println("该房间空闲状态,不接受退订!"); } @Override public void checkInRoom() { System.out.println("您已经入住..."); // 房间状态切换为已入住状态 hotelManager.setState(hotelManager.getCheckInState()); } @Override public void checkOutRoom() { System.out.println("该房间空闲状态,不接受退房!"); } }
入住状态
package state; /** * * Title: CheckInState Description: 入住状态 (只能退房) * * @author yacong_liu Email:2682505646@qq.com * @date 2019年1月16日 */ public class CheckInState implements State { Room hotelManager; public CheckInState(Room hotelManager) { this.hotelManager = hotelManager; } @Override public void bookRoom() { System.out.println("该房间处于入住状态,不再接受预定!"); } @Override public void unsubscribeRoom() { System.out.println("该房间处于入住状态,不能退订!"); } @Override public void checkInRoom() { System.out.println("该房间处于入住状态,不能在入住!"); } @Override public void checkOutRoom() { System.out.println("退房成功..."); // 房间状态切换为空闲状态 hotelManager.setState(hotelManager.getFreeState()); } }
预订状态
package state; /** * * Title: BookedState Description: 预定状态(可退订 入住) * * @author yacong_liu Email:2682505646@qq.com * @date 2019年1月16日 */ public class BookedState implements State { Room hotelManager; public BookedState(Room hotelManager) { this.hotelManager = hotelManager; } @Override public void bookRoom() { System.out.println("该房间已经处于预定状态,不在接受预定!"); } @Override public void unsubscribeRoom() { System.out.println("退订成功,欢迎下次光临"); // 房间状态切换为空闲状态 hotelManager.setState(hotelManager.getFreeState()); } @Override public void checkInRoom() { System.out.println("入住成功..."); // 房间状态切换为入住状态 hotelManager.setState(hotelManager.getCheckInState()); } @Override public void checkOutRoom() { System.out.println("该房间处于预定状态,不接受退房!"); } }
客户端测试
package state; import org.junit.Test; public class RoomTest { @Test public void test() { Room[] room = new Room[2]; for (int i = 0; i < room.length; i++) { room[i] = new Room(); } room[0].bookRoom(); room[0].checkInRoom(); room[0].bookRoom(); room[0].checkOutRoom(); } }
输出结果
您已经成功预订了...
入住成功...
该房间处于入住状态,不再接受预定!
退房成功...
通过上面的例子,我们已经对状态模式有所了解,下面我们做一个总结,来回顾我们的状态模式:
1.状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
理解:这个模式将状态封装成独立的类,并将动作委托到代表当前状态的对象,这就是说行为会随着内部状态而改变。
“看起来好像修改了它的类”是什么意思呢?从客户的视角来看:如果说你使用的对象能够完全改变它的行为,那么你会觉得,这个对象实际上是从别的类实例化而来的。然而,实际上,你知道我们是在使用组合通过简单引用不同的状态对象来造成类改变的假象
2.状态模式要点
(1)客户不会和状态进行交互,全盘了解状态是 context的工作
(2)在状态模式中,每个状态通过持有Context的引用,来实现状态转移
(3)使用状态模式总是会增加设计中类的数目,这是为了要获得程序可扩展性,弹性的代价,如果你的代码不是一次性的,后期可能会不断加入不同的状态,那么状态模式的设计是绝对值得的。【同时也是一个缺点】
(4)状态类可以被多个context实例共享