定义
状态模式(State Pattern)指的是将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化,在其内部状态改变时改变它的行为。状态模式是一种对象行为型模式。它和策略模式有一点很像,就是将一些复杂的逻辑放在一个专门的上下文类中进行处理。
往往在一个系统中的某个对象会存在多个状态,而且这些状态之间可以进行转换,并且在不同的状态下会具有不同的行为或者功能,比如很多喜欢看电视的朋友有时候用腾讯视频看电视,如果不充值会员的话,会有广告,而且很多vip的节目也不能观看了,冲了会员就没了广告,而且也能解锁vip节目,这时候用户就拥有两个状态,一个是vip状态,一个是非vip状态,而且在不同的状态下也有不同的行为,这时候的系统设计就可以使用状态模式。
组成部分
状态模式包含以下三个主要部分:
1、抽象状态类(State):它主要定义了一个公共接口或者抽象方法,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
2、具体状态类(Concrete State):它主要就是继承或者实现抽象状态类/接口,并且实现抽象状态所对应的行为,在需要的情况下进行状态切换。
3、环境类(Context):也叫上下文,它内部维护一个当前状态,并负责具体状态的切换,最终给客户端调用。
例子
首先定义一个抽象的状态类,并且有抽象的行为方法:
public abstract class State { public abstract void Do(); } 复制代码
接着定义两个具体的状态子类去继承抽象的状态类,并且实现抽象方法:
public class ConcreteState1 extends State { @Override public void Do() { System.out.println("吃跑了,来外面散散步"); } } public class ConcreteState2 extends State { @Override public void Do() { System.out.println("肚子很饿,需要吃饭"); } } 复制代码
接着定义一个上下文类,即环境类,用来处理状态和行为:
public class Context { // 维护一个抽象状态对象的引用 private State state; public State getState() { return state; } public void setState(State state) { this.state = state; } public boolean isHungry() { return isHungry; } public void setHungry(boolean hungry) { isHungry = hungry; } private boolean isHungry; private void checkStates(){ if(isHungry){ //如果饿了的状态需要做饭 setState(new ConcreteState2()); }else{ //吃饱了,出去走走 setState(new ConcreteState1()); } } public void process(){ checkStates(); state.Do(); } } 复制代码
测试方法:
public class StatePatternTest { public static void main(String[] args) { Context context = new Context(); //设置状态为饿了 context.setHungry(true); context.process(); //设置状态为不饿 context.setHungry(false); context.process(); } } 复制代码
运行结果:
上面状态的处理,切换改变,除了可以在上下文类中实现,还可以在具体的状态子类中实现,比如:
首先需要在上下文类中初始化一个状态实例对象,并且将上下文对象作为子类的状态的构造参数传递给具体的子类中,上下文类代码:
// 设置初始状态 this.state = new ConcreteStateA(this); 复制代码
然后在具体的子类状态类中根据构造进来的上下文类实例对象,通过调用它的属性值进行业务逻辑判断 进行状态的检查和切换。比如在ConcreteState1类的代码修改如下:
public class ConcreteState3 extends State { private Context context; public ConcreteState3(Context context){ context= context; } @Override public void Do() { System.out.println("吃跑了,来外面散散步"); checkState(); } /** * 检查状态 是否需要进行状态的转换<br/> * 状态的切换由具体状态子类中实现 */ private void checkState(){ if (context.getisHungry()) { context.setState(new ConcreteState1(context)); } } } 复制代码
状态模式的优点
1、状态模式的代码结构清晰,它将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,体现了“单一职责原则”。
2、将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
3、每个具体的状态类负责具体的职责,有利于程序的扩展。可以增加新的子类来增加新的状态和行为。
1、同状态模式的第三个优点,拓展的同时会增加代码量,每增加一个新的子类,都会增加代码中类的个数;
2、状态模式符合单一职责原则,但是对“开闭原则”的支持并不太好,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
应用场景
1、对象的行为依赖于它的状态并且可以根据它的状态改变而改变它的相关行为,比如前面说的会员系统;
2、一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。比如常见的if else结构的代码,或者switch语句等等。
总结
状态模式总结起来是如果一个对象的行为依赖于状态或者属性,那么可以将状态和行为拆分封装在一个环境类中处理,然后具体的子类负责具体行为。
最后本文以及之前的所有的设计模式中的例子代码,都将同步至github,需要的欢迎下载star。