前言
- 在我看来,状态模式在设计模式中算是比较复杂的了,而且这个模式并不怎么优秀
- 本篇我将举两个例子来说明用不用状态模式对程序结构上的影响,以及状态模式所带来的好处
- 由于状态模式和策略模式类图基本一致,因此文末将做一些对比
情景引入
首先我们先看一个没加状态模式的简单例子,支付宝里有个小游戏叫蚂蚁庄园,我在里面已经捐满了500颗爱心,获得了四个勋章,但是竟然有人都捐700颗爱心了,不知道他们是怎么做到的,深切怀疑他们是人民币玩家,不过这跟举例无关啦。以蚂蚁庄园小鸡为例,小鸡在吃饭的过程中,可以邀请别的小鸡来吃,也可以把来吃饭的小鸡送走,以这两个功能写一个简单例子。
java源程序
蚂蚁庄园
由于小鸡吃饭的时候可以邀请别的小鸡,但是一旦有别的访客了,就不能再邀请了,同样有访客的时候我们可以把访客送走,但是没有访客的时候就不能执行送走功能。因此为了判断小鸡有没有访客我们就用两种状态表示。
package state;
public class AntFarm {
static int eatstate = 0;//表示自己吃饭状态
static int vistitorstate = 1;//表示有访客状态
static int state;
public AntFarm() {
state = 0;//初始化时小鸡为吃饭状态
}
public void invite() {
if (state == 0) {
System.out.println("邀请别的小鸡");
state = vistitorstate;//状态修改
} else if (state == 1) {
System.out.println("已有访客,请勿重复邀请");
}
}
public void expel() {
if (state == 1) {
System.out.println("把访客送走");
state = eatstate;
} else if (state == 0) {
System.out.println("当前没有访客");
}
}
}
客户端
package state;
public class Client {
public static void main(String[] args) {
AntFarm af=new AntFarm();//初始化的小鸡在吃饭
af.invite();//邀请访客
af.expel();//送走访客
af.expel();
af.invite();
}
}
运行结果
程序分析
- 这个程序理解起来并不难,但是我们需要从中理解一下状态模式使用的地方(当然还没用到),即当对象在不同状态(情景)的情况下会做不同的行为,例如小鸡在没有访客的情况下可以邀请访客,但是不能去送走访客。这就是一个情景,有多个状态也是状态模式可以使用的条件。
- 本例中有两种状态,分别是自己吃,和访客来访,因此方法中区分状态用条件语句还比较简洁,可是当对象有多种状态的时候再用条件语句显然很不好了,但引入状态模式可以省去条件语句的判断
- 在本例中对于状态的扩展也极其困难,因为增加别的状态,所有方法里的条件语句都要更改,因此不符合开闭原则。
下面改成状态模式
状态模式的基本角色
环境类(context) | 里面维护了一个具体状态的实例(AntFarmContext) |
抽象状态类(state) | 定义一个接口,封装一些特定的行为(AntFarmState) |
具体状态类(concreteState) | 每个子类实现一个与context的一个状态相关的行为(EatingState,InvistorState) |
引入状态模式的程序类图
为了符合一般状态模式的样子,有些关系就没画
java源程序
本次我们把两种状态单独拆分出来封装成类,而状态在程序运行中发生动态改变,而执行程序则是由context把状态委托给响应的状态类来执行
环境类
package state;
public class AntFarmContext {
AntFarmState state;//持有一个具体状态的实例,不一定是哪种状态
public AntFarmState getState() {
return state;
}
public void setState(AntFarmState state) {
this.state = state;
}
public void invite(){
state.invite();//委托给具体状态类运行
}
public void expel(){
state.expel();
}
}
抽象状态类
package state;
public abstract class AntFarmState {
//写这两个方法就是为了子类完成相应操作中重写
public void invite(){
System.out.println("已有访客,请勿重复邀请");
}
public void expel(){
System.out.println("当前没有访客");
}
}
状态子类(吃饭状态)
package state;
public class EatingState extends AntFarmState{
AntFarmContext afc;
public EatingState(AntFarmContext afc) {
this.afc=afc;
}
public void invite() {
System.out.println("邀请别的小鸡");
//执行相应操作后在环境类中更改所处的状态,邀请了小鸡此时给环境类设置访客状态
afc.setState(new InvistorState(afc));
}
}
状态子类(访客状态)
package state;
public class InvistorState extends AntFarmState{
AntFarmContext afc;
public InvistorState( AntFarmContext afc) {
this.afc=afc;
}
public void expel() {
System.out.println("把访客送走");
afc.setState(new EatingState(afc));
}
}
客户端
package state;
public class Client1 {
public static void main(String[] args) {
AntFarmContext afc=new AntFarmContext();
EatingState es=new EatingState(afc);
afc.setState(es);//环境类设置最初状态
afc.invite();
afc.expel();
afc.expel();
afc.invite();
}
}
运行结果
程序分析
-
- 该程序只所以能应用模式不是因为我故意这样干的,是因为我故意找的这种例子,这种例子有很多,手机有点你能打游戏,手机没电你还能打吗,因此不同状态(情景)下会产生相应的行为,所以才能应用状态模式。
- 这个程序本身也是很简单的,但是用了状态模式之后却变得难以理解了,尤其是其中的状态改变关系和委托关系,不过理解了运行步骤在理解这个模式就会变得简单多了。
- 该程序吧之前的程序拆分了四个类,两种状态,但是程序中不在有条件语句了 ,类变多了,但结构分明了
- 该例在引入新的状态也比原来简单,但是由于状态之间是相关的,因此引入状态免不了修改原有的代码。
下面我们认识一下状态模式的一些基本概念
定义
状态模式允许对象在内部状态改变的时候改变他的行为,对象看起来像被修改了类
应用场景
很简单,当程序中确实有很多场景,不同场景又有相应行为的时候最适合用
状态模式优点
-
- 应用状态模式使程序扩展起来变的简单
- 避免了多重条件语句的应用
- 程序结构会变得比较清晰
缺点
-
- 应用状态模式使类变的过多,而且简单的关系会变的理解困难
- 没有很好的遵守开闭原则,引入新的新的状态会导致原有状态的修改
- 没有很好的处理耦合关系,从例子中可以看出,应用状态模式不仅会使状态类和环境类进行关联而且状态类之间也有关系存在
- 因此我感觉跟其他模式相比状态模式不算是一个很好的设计
状态模式和策略模式的对比
比较点 | 状态模式 | 策略模式 | 比较结果 |
类图 |
|
| 相同 |
所做的事 | 都是去做指定的行为 |
| 相同 |
怎么做 | 看状态,再做事 | 客户端指定什么就做什么 | 不同 |
行为的变化 | 行为执行过程中会引发状态改变,行为也会改变 | 客户端指定什么行为就做什么 | 不同 |
客户端与行为的关系 | 客户端不知道具体行为 | 客户端必须去知道所有的行为 | 不同 |
如果不是很清楚策略模式可以看我的策略模式的博客
传送门:https://blog.csdn.net/zhen921/article/details/81810801
后记
初次 了解状态模式的时候,它的程序结构真的把我搞晕了,不过把程序运行逻辑理理清楚也就明白了,不过设计模式要想理解就得多费费脑子了。