状态模式的定义为:当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了它的类。
状态模式比较容易理解: 就是把各个状态定义成类,对状态的更改封装到状态类里,对更改进行的限制也封装到相应的类里。状态模式的好处就是结构清晰,封装性比较好,也容易扩展。
网上很多例子都是用红绿灯来做状态模式的例子。指示灯有红黄绿三种颜色,相当于三种状态,然后定义三个状态类,在三个状态类里定义指示灯的改变。这样比较清晰,比如说红灯里面的改变颜色方法就是变成绿灯。个人觉得这个例子不太好。。。 比如我完全可以这样:
changeState(int i){ return (i + 1)%3; }
然后用三个数字代表三种颜色,好像更好更简单。。。
个人觉得状态模式更适合这种场景: 多种状态,多种操作,并且每种操作对应的状态改变都有限制。 比如说设计之禅上面的电梯例子,再比如说我下面代码里的订房间的例子。 房间有多种状态,这里暂定三种:空闲,被预订,已入住。 如果不用状态模式的话,那么订房操作就是各种if--else判断。如果逻辑再复杂点,如果状态再多点,如果针对每种状态的逻辑差异再大点。。。。 一路if-else搞下去,一个方法上千行不是梦。。。。 这样编码的时候可能不觉得比状态模式好多少,因为你状态模式还有很多类呢,也是逻辑复杂,代码一点不比我少。但是。。 如果后期维护呢,我是见过一个方法500行代码的,要想修改一下,真的能把人逼疯。。。。
状态模式个人觉得特别容易理解,代码也清晰简单,一个状态接口,定义改变状态的各种操作:
/** * State:状态接口 * @author xuejupo [email protected] * create in 2015-12-30 下午2:46:59 * */ interface State{ /** * bookRoom: 预定房间 * void 返回类型 */ void bookRoom(); /** * unbookRoom: 取消预定 * void 返回类型 */ void unbookRoom(); /** * checkinRoom: 入住房间 * void 返回类型 */ void checkinRoom(); /** * checkoutRoom: 退房 * void 返回类型 */ void checkoutRoom(); }
然后就是各种实体状态类:
/** * FreeState: 房间的空闲状态 * @author xuejupo [email protected] * create in 2015-12-30 下午2:54:00 * */ class FreeState implements State{ private HotelRoom room; public FreeState(HotelRoom room){ this.room = room; } @Override public void bookRoom() { // TODO Auto-generated method stub System.out.println("房间空闲可预定,您已经订了这个房间"); //改变房间状态 this.room.setState(this.room.getIsBookedState()); } @Override public void unbookRoom() { // TODO Auto-generated method stub System.out.println("房间空闲,不能取消预定"); } @Override public void checkinRoom() { // TODO Auto-generated method stub System.out.println("房间空闲,可以直接入住,只要交钱就行"); this.room.setState(this.room.getCheckInState()); } @Override public void checkoutRoom() { // TODO Auto-generated method stub System.out.println("房间空闲,不能直接退房"); } } /** * IsBooked: 房间处于已被预定状态 * @author xuejupo [email protected] * create in 2015-12-30 下午2:55:26 * */ class IsBookedState implements State{ private HotelRoom room; public IsBookedState(HotelRoom room){ this.room = room; } @Override public void bookRoom() { // TODO Auto-generated method stub System.out.println("房间已经被预定"); } @Override public void unbookRoom() { // TODO Auto-generated method stub System.out.println("房间已经被预定,可以取消预定"); this.room.setState(this.room.getFreeState()); } @Override public void checkinRoom() { // TODO Auto-generated method stub System.out.println("房间已预定,可以入住"); this.room.setState(this.room.getCheckInState()); } @Override public void checkoutRoom() { // TODO Auto-generated method stub System.out.println("房间已预定状态,不能直接退房"); } } /** * CheckInState: 房间是已入住状态 * @author xuejupo [email protected] * create in 2015-12-30 下午2:56:25 * */ class CheckInState implements State{ private HotelRoom room; public CheckInState(HotelRoom room){ this.room = room; } @Override public void bookRoom() { // TODO Auto-generated method stub System.out.println("房间住人了,不能再预定了"); } @Override public void unbookRoom() { // TODO Auto-generated method stub System.out.println("房间住人了,取消预定了"); } @Override public void checkinRoom() { // TODO Auto-generated method stub System.out.println("房间住人了,不能再入住了"); } @Override public void checkoutRoom() { // TODO Auto-generated method stub System.out.println("您可以正常退房!"); //改变房间状态到空闲 this.room.setState(this.room.getFreeState()); } }
房间接口(状态更改操作的实施者接口):
/** * Room:房间接口 * @author xuejupo [email protected] * create in 2015-12-30 下午2:46:59 * */ interface Room{ /** * bookRoom: 预定房间 * void 返回类型 */ void bookRoom(); /** * unbookRoom: 取消预定 * void 返回类型 */ void unbookRoom(); /** * checkinRoom: 入住房间 * void 返回类型 */ void checkinRoom(); /** * checkoutRoom: 退房 * void 返回类型 */ void checkoutRoom(); }
具体房间类:
/** * HotelRoom: 旅馆房间 * @author xuejupo [email protected] * create in 2015-12-30 下午3:06:08 * */ class HotelRoom implements Room{ /* * 房间的四个状态 */ private State freeState; //空闲状态 private State checkInState; //入住状态 private State isBookedState; //预订状态 private State state; //房间现有的状态 //在构造函数里面初始化 public HotelRoom(){ this.freeState = new FreeState(this); this.checkInState = new CheckInState(this); this.isBookedState = new IsBookedState(this); this.state = new FreeState(this); } @Override public void bookRoom() { // TODO Auto-generated method stub this.state.bookRoom(); } @Override public void unbookRoom() { // TODO Auto-generated method stub this.state.unbookRoom(); } @Override public void checkinRoom() { // TODO Auto-generated method stub this.state.checkinRoom(); } @Override public void checkoutRoom() { // TODO Auto-generated method stub this.state.checkoutRoom(); } public final State getFreeState() { return freeState; } public final State getCheckInState() { return checkInState; } public final State getIsBookedState() { return isBookedState; } public final State getState() { return state; } public final void setState(State state) { this.state = state; } }
测试代码:
/** * StateTest : 状态模式 * @author xuejupo [email protected] * create in 2015-12-30 下午2:46:43 */ public class StateTest { /** * main: * @param args * void 返回类型 */ public static void main(String[] args) { // TODO Auto-generated method stub //初始化一个房间 Room room = new HotelRoom(); room.bookRoom(); room.bookRoom(); room.checkinRoom(); room.bookRoom(); room.checkoutRoom(); room.checkinRoom(); room.bookRoom(); } }
结果:
房间空闲可预定,您已经订了这个房间 房间已经被预定 房间已预定,可以入住 房间住人了,不能再预定了 您可以正常退房! 房间空闲,可以直接入住,只要交钱就行 房间住人了,不能再预定了
清晰明白!
ps(个人感受):
第一次接触状态模式的示例代码是红绿灯那个实例。个人觉得那个实例不好,容易把人搞晕。。。我当初学状态模式的时候就被他搞得挺晕。。。 当时觉得,红绿灯这么简单的问题也用这么多类。。。。 这不是把简单问题复杂化了嘛。。。
如果老大让我做一个红绿灯的项目,我多半不会用状态模式。第一:他的状态特别少,就三个状态,要是用状态模式就要多写三个类,还有一个接口。。 如果不用状态模式,我一个int搞定;第二,他的状态之间转换特别简单,我完全可以一个方法搞定,最多多写点注释,比多用几个类要简单清晰得多。而且,感觉后期维护看起来也比较清晰;第三,红绿灯项目是不用考虑增加状态的,像旅店房间可能会增加几个状态,比如未打扫状态,或者预定未付定金状态等等,但是红绿灯不用考虑这些,所以设计的时候可以不考虑扩展性。