定义

状态模式(State),当一个对象的内部状态改变时允许改变其行为,这个对象看起来像是改变了其类。

类图

cYqyRcVcaC4AAAAASUVORK5CYII=

  • Context:环境,上下文,其实就是状态管理者,隐藏了状态之间转换的细节,是客户端与各个状态的中间人。request()方法是客户端调用的,
  • State:抽象状态,定义了状态的处理抽象方法handle(),各个具体状态需要实现它。
  • ConcreteState:具体状态,实现了状态的公共方法,且包含了状态转换的判断。

经典源码

状态管理者

public class Context {
    //持有一个State类型的对象实例
    private State state;

    public void setState(State state) {
        this.state = state;
    }
    /**
     * 客户端调用
     */
    public void request(String sampleParameter) {
        //转调state来处理
        state.handle(sampleParameter);
    }
}

抽象状态

public interface State {
    /**
     * 状态对应的处理
     */
    public void handle(String sampleParameter);
}

具体状态A

public class ConcreteStateA implements State {

    @Override
    public void handle(String sampleParameter) {
        
        System.out.println("ConcreteStateA handle :" + sampleParameter);
    }

}

具体状态B

public class ConcreteStateB implements State {

    @Override
    public void handle(String sampleParameter) {
        
        System.out.println("ConcreteStateB handle :" + sampleParameter);
    }

}
客户端调用方
public class Client {

    public static void main(String[] args){
        //创建状态
        State state = new ConcreteStateB();
        //创建环境
        Context context = new Context();
        //将状态设置到环境中
        context.setState(state);
        //请求
        context.request("test");
    }
}

实际案例

背景

小A正在召唤师峡谷厮杀,他玩的是卡特,买了一本杀人书。随着游戏时间的进行,小A杀的人越来越多,且自己都没死过,游戏中不断传来“XXX暴走了”、“XXX主宰了比赛”、“XXX接近神了”。最终小A以12杀0死的完美战绩结束了比赛,脑海中刚才的捷报声还在不停环绕在他的耳边,他就想这不就是状态模式,随后打开IDE开始复现刚才的过程。

结构

环境类

package com.jo.state;

/**
 * @author Jo
 * @date 2018/1/17
 */
public class LolContext {
    /**
     * 当前杀人状态
     */
    private KillState killState;

    /**
     * 当前杀人数
     */
    private Integer killNum = 0;

    public LolContext() {
        killState = new Normal();
    }

    public Integer getKillNum() {
        return killNum;
    }

    public LolContext setKillState(KillState killState) {
        this.killState = killState;
        return this;
    }

    /**
     * 杀人方法,
     */
    public void kill(){
        killNum += 1;
        killState.kill(this);
        System.out.println("当前击杀数" + killNum);
        System.out.println();
    }
}

抽象杀人状态

public interface KillState {
    /**
     * 抽象杀人方法
     * @param lolContext
     */
    void kill(LolContext lolContext);
}

普通击杀

public class Normal implements KillState {
    @Override
    public void kill(LolContext lolContext) {
        System.out.println("你杀了一个人");
        //杀了2个人的时候转换状态
        if (lolContext.getKillNum() > 1){
            lolContext.setKillState(new KillingSpring());
        }
    }
}

大杀特杀

public class KillingSpring implements KillState {
    @Override
    public void kill(LolContext lolContext) {
        System.out.println("你正在大杀特杀");
        lolContext.setKillState(new Rampage());
    }
}

接近暴走

public class Rampage implements KillState {
    @Override
    public void kill(LolContext lolContext) {
        System.out.println("你已经接近暴走了");
        lolContext.setKillState(new Unstoppable());
    }
}

无人可挡

public class Unstoppable implements KillState {
    @Override
    public void kill(LolContext lolContext) {
        System.out.println("你已经无人可挡了");
        lolContext.setKillState(new Dominating());
    }
}





超神

public class Legendary implements KillState {
    @Override
    public void kill(LolContext lolContext) {
        System.out.println("你已经超神了");
    }
}

客户端调用

public class Client {
    public static void main(String[] args) {
        LolContext lolContext = new LolContext();
        for (int i = 0; i < 13; i++) {
            lolContext.kill();
        }
    }
}

运行结果
图片描述

客户端只负责调用LolContext的kill方法,其余一概不知,内部便会随着杀人数的增长打印不同的通知。而LolContext中也没有复杂,一大串的if else switch case,状态的转移在各自的具体状态中,如果需要修改部分逻辑只需要改对应的状态,而不需要改原本在一起的if else,大大减少了隐患的发生率,从而不会发生牵一发而动全身的结果。

适用场景

状态模式的优点是解除了程序的耦合度,采用子类的方式去除了烦琐容易出错的if else,但反而带来的是类的数据增多。如果你要实现的功能状态不多,且功能简单,那不推荐使用状态模式,不然会徒增程序的复杂性。且要执行的动作有一定的复杂度,此例的kill方法是最简单的实现,实际应用中复杂度是远远大于它的。你可以想象在状态多,且复杂的动作中不使用状态模式会事怎样,if else多的眼花缭乱,上一个if和下一个else if相差几百行代码,想必这样的代码谁都不愿意碰,万一改坏了就要背锅了。

总结

状态模式和和策略模式有点相像,状态模式的状态转移是内部控制的,而策略模式是由客户端控制采用不同的策略。因此在目的和实现还是有很大的差别的。有些场景“状态”不是那么明显,需要转换成状态模式就考察使用者的功底和对业务的理解程度了,望大家都能get更多知识点,能力越来越强,个个都是架构师。


请叫我程序猿大人
19 声望2 粉丝