(十六)状态模式(State)

最后更新于:2022-04-01 15:48:39

状态模式(State)就是根据对象的状态不同,将有不同的行为。很简单的方法我们可以做N个 ~~~ if(){ } else if(){ } ... ... else{ } ~~~ 但是这样可想而知工作量会相当的大,这样就引入了状态模式,能实现和前面一样的功能,但是没有判断语句,而且如果添加了新的功能模块或者是流程,只要添加一个状态类就可以了。下面是简单的状态模式的原理图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-12_5784560a18132.gif) 这里我用了一个QQ登录的状态来简单的模拟了一下状态模式,因为QQ从离线到登录到忙碌到离线就是一个很好的状态模式: 简单的画了一个原理图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-12_5784560a2bc34.gif) 首先建立一个state接口,主要用于实现用,通过接口编程方便: ~~~ package com.designpattern.state; public interface State { public void handle(QQ qq); } ~~~ 而后分别建立离线状态,Q我吧状态,忙碌状态,然后又回到离线,具体内容是一样的,就是一个状态里面的handle方法创建了下一个状态从而达到状态不断切换的功能: ~~~ package com.designpattern.state; public class LeavingState implements State { @Override public void handle(QQ qq) { qq.setState(new LoginningState()); System.out.println("QQ正在登陆在中······"); } } ~~~ ~~~ package com.designpattern.state; public class LoginningState implements State { @Override public void handle(QQ qq) { qq.setState(new ChattingState()); System.out.println("QQ登陆成功,Q我吧······"); } } ~~~ ~~~ package com.designpattern.state; public class ChattingState implements State { @Override public void handle(QQ qq) { qq.setState(new BusyingState()); System.out.println("QQ正在忙碌中······"); } } ~~~ ~~~ package com.designpattern.state; public class BusyingState implements State { @Override public void handle(QQ qq) { qq.setState(new LeavingState()); System.out.println("QQ已经离线······"); } } ~~~ 这样定义一个QQ类,主要是对QQ的各种状态的一个动态的变化,废话不多说,看代码就明了: ~~~ package com.designpattern.state; public class QQ { private State state; public QQ() { state = new LeavingState(); } public State getState() { return state; } public void setState(State state) { this.state = state; } public void handle() { state.handle(this); } } ~~~ 然后客户端调用输出: ~~~ package com.designpattern.state; public class Client { public static void main(String[] args) { QQ qq = new QQ(); qq.handle(); qq.handle(); qq.handle(); qq.handle(); qq.handle(); qq.handle(); qq.handle(); qq.handle(); } } ~~~ ~~~ QQ正在登陆在中······ QQ登陆成功,Q我吧······ QQ正在忙碌中······ QQ已经离线······ QQ正在登陆在中······ QQ登陆成功,Q我吧······ QQ正在忙碌中······ QQ已经离线······ ~~~ 这样就简单的模拟了QQ的一系列的状态; 状态模式使代码种复杂而庸长的逻辑判断语句问题得到了解决,而且具体状态角色将具体的状态和它对应的行为封装起来了,这使得增加一种新的状态变得十分简单。但是每一个状态对应一个具体的状态类,是结构分散,逻辑不是很清楚,阅读代码工作量会大一些。 看到这里我不禁思考了一个问题,既然用状态模式可以模拟QQ的一系列的状态,那么我的状态的改变也一样能通知好友,让好友时时的关注我的状态情况,这时我就想到了刚刚学到的Observer模式,就是一个观察啊,我就想了一下,把两个模式用到一起,把每一个状态的变化都通知自己所有的好友,不多说,马上做的Demo试试: 和上面一样首先建立了一个公用的接口State.java 接着模式QQ的四种状态,这里就粘贴了一个实例,不过都差不多: ~~~ package com.designpattern.state_observer; public class ChattingState implements State { @Override public void handle(QQ qq) { qq.setState(new BusyingState()); qq.setMessage("QQ正在忙碌中······"); } } ~~~ 和上面不同的是堕落一个setMessage,这里是用来传递给QQ类,这样才能再次提醒观察的QQ好友,我的变化; 这里QQ类我定义成了一个抽象的类,不过大部分都封装好了,在让子类去继承的时候就继承一个handle方法就能实现操作,这样看起来比较简单: ~~~ package com.designpattern.state_observer; import java.util.ArrayList; import java.util.List; public abstract class QQ { private List<Friends> friends = new ArrayList<Friends>(); private String message; private String name = "halberd"; private State state; public QQ() { this.state = new LeavingState(); } public synchronized QQ addFriend(Friends friend) { friends.add(friend); return this; } public String getMessage() { return message; } public State getState() { return state; } public QQ handle() { this.state.handle(this); System.out.println(this.message); this.notifyFriends(); return this; } public void notifyFriends() { for (int i = 0; i < friends.size(); i++) { Friends friend = (Friends) friends.get(i); friend.recevie(this.message, this.name); } } public synchronized QQ removeFriend(Friends friend) { friends.remove(friend); return this; } public void setMessage(String message) { this.message = message; } public void setState(State state) { this.state = state; } } ~~~ 其实很简单,就是把两个模式结合了一个,之后就是具体的实例对象: ~~~ package com.designpattern.state_observer; public class MyQQ extends QQ { @Override public QQ handle() { return super.handle(); } } ~~~ 我起名为MyQQ意为我的QQ,然后在用我的QQ好友实现Friends接口: ~~~ package com.designpattern.state_observer; public interface Friends { public void recevie(String message,String name); } ~~~ 里面的两个参数一个是状态,一个是用户名(其实一个不写也行,我是想如果观察的不是我,就又可以复用了) 然后定义了两个朋友: ~~~ package com.designpattern.state_observer; public class AndiMuise implements Friends { @Override public void recevie(String message, String name) { System.out.println("AndiMuise已经知道 " + name + " : " + message); } } ~~~ 客户端操作如下: ~~~ package com.designpattern.state_observer; public class Client { public static void main(String[] args) { QQ qq = new MyQQ(); qq.addFriend(new AndiMuise()).addFriend(new Steve()); qq.handle().handle().handle(); } } ~~~ 实例一个QQ对象,这里当然是我自己了,MyQQ这里如果是别人的直接再继承一下就行了,也不用修改代码,之后添加了两个QQ好友作为监听者,然后开始我一系列的状态的变化: ~~~ QQ正在登陆在中······ AndiMuise已经知道 halberd : QQ正在登陆在中······ Steve已经知道 halberd : QQ正在登陆在中······ QQ登陆成功,Q我吧······ AndiMuise已经知道 halberd : QQ登陆成功,Q我吧······ Steve已经知道 halberd : QQ登陆成功,Q我吧······ QQ正在忙碌中······ AndiMuise已经知道 halberd : QQ正在忙碌中······ Steve已经知道 halberd : QQ正在忙碌中······ ~~~ 这样就大功告成了,其实这样挺好的,慢慢学会把几个模式综合一下,因为重复的代码比较多,没有贴全,上传到了: [http://download.csdn.net/detail/wclxyn/4230476](http://download.csdn.net/detail/wclxyn/4230476) 初学笔记,还望批评指正 (后来改成了这个样子,就是能调用了setName方法,当然返回值是QQ,这样就省事了,关键是我比较懒) ~~~ package com.designpattern.state_observer; public class Client { public static void main(String[] args) { QQ qq = new MyQQ(); qq.setName("wclxyn").addFriend(new AndiMuise()).addFriend(new Steve()) .handle().handle().handle(); new MyQQ().setName("腾讯客服").addFriend(new AndiMuise()).addFriend( new Steve()).handle().handle().handle(); } } ~~~ ~~~ QQ正在登陆在中······ AndiMuise已经知道 wclxyn : QQ正在登陆在中······ Steve已经知道 wclxyn : QQ正在登陆在中······ QQ登陆成功,Q我吧······ AndiMuise已经知道 wclxyn : QQ登陆成功,Q我吧······ Steve已经知道 wclxyn : QQ登陆成功,Q我吧······ QQ正在忙碌中······ AndiMuise已经知道 wclxyn : QQ正在忙碌中······ Steve已经知道 wclxyn : QQ正在忙碌中······ QQ正在登陆在中······ AndiMuise已经知道 腾讯客服 : QQ正在登陆在中······ Steve已经知道 腾讯客服 : QQ正在登陆在中······ QQ登陆成功,Q我吧······ AndiMuise已经知道 腾讯客服 : QQ登陆成功,Q我吧······ Steve已经知道 腾讯客服 : QQ登陆成功,Q我吧······ QQ正在忙碌中······ AndiMuise已经知道 腾讯客服 : QQ正在忙碌中······ Steve已经知道 腾讯客服 : QQ正在忙碌中······ ~~~
';