(十六)状态模式(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正在忙碌中······
~~~