学好设计模式防被祭天:状态模式

2017-09-03  本文已影响41人  阿菜的博客
状态模式

为了防止被“杀”了祭天,学点设计模式,并总结下还是有必要的。

一:理解

  1. 状态模式从另一个角度思考状态转移问题。
  2. 原有逻辑是实体的状态从A变成B。在状态模式中,状态转义过程被抽象成从处于状态A的实体变成处于状态B的实体。
  3. 可以抽象出多个包含实体的状态类。

二:例子

你是个富二代。

你的生活状态很简单,一种是充满能量状态ENERGETIC_STATE,还有一种就是贤者时间状态XIANZHE_STATE。

当你在贤者时间时,只要休息了,就会变得活力满满。

当你在充满能量状态时,只要papapa了,就会进入贤者时间。

当你在贤者时间,就没有兴趣接着再papapa了。

当你活力满满,就不需要再休息了。

于是,你叫来程序员小菜帮你抽象一下你的生活状态。

小菜上来就是一顿敲。

@Data
public class FuErDai {
    private static final int XIANZHE_STATE = 0;
    private static final int ENERGETIC_STATE = 1;

    private int state;

    public FuErDai(int state) {
        this.state = state;
    }

    public void rest() {
        if (state == XIANZHE_STATE) {
            System.out.println("当前状态是贤者时间,需要休息!");
            state = ENERGETIC_STATE;
        } else if (state == ENERGETIC_STATE) {
            System.out.println("当前状态是能量满满,不需要休息!");
        }
    }

    public void papapa() {
        if (state == XIANZHE_STATE) {
            System.out.println("当前状态是贤者时间,不想papapa!");
        } else if (state == ENERGETIC_STATE) {
            System.out.println("当前状态是能量满满,来一发!");
            state = XIANZHE_STATE;
        }
    }
}

这是一个很简单的程序,FuErDai类中含有状态属性state,每次想要休息rest或者papapa时,使用if else对当前状态进行判断。

测试程序:

public class ClientV1 {
    public static void main(String[] args) {
        FuErDai fuErDai = new FuErDai(0);
        fuErDai.papapa();
        fuErDai.papapa();
        fuErDai.rest();
        fuErDai.rest();
        fuErDai.papapa();
        fuErDai.rest();
    }
}

输入/输出:

当前状态是贤者时间,不想papapa!
当前状态是贤者时间,不想papapa!
当前状态是贤者时间,需要休息!
当前状态是能量满满,不需要休息!
当前状态是能量满满,来一发!
当前状态是贤者时间,需要休息!

这段程序简单易懂,你很开心。

有一天,你突然发现,自己在充满能量和贤者时间以外还有一个状态,就是一半一半状态HALF_STATE。

在一半一半状态时候:

  1. 你尝试休息,会有百分之五十的概率开始休息,休息完进入活力满满状态。
  2. 你尝试papapa,会有百分五十的概率接受约X,papapa之后进入贤者时间。

小菜觉得不就是多了一个状态而已,分分钟搞定。

@Data
public class FuErDaiV2 {
    private static final int XIANZHE_STATE = 0;
    private static final int ENERGETIC_STATE = 1;
    private static final int HALF_STATE = 2;

    private int state;

    public FuErDaiV2(int state) {
        this.state = state;
    }

    public void rest() {
        if (state == XIANZHE_STATE) {
            System.out.println("当前状态是贤者时间,需要休息!");
            state = ENERGETIC_STATE;
        } else if (state == ENERGETIC_STATE) {
            System.out.println("当前状态是能量满满,不需要休息!");
        } else if (state == HALF_STATE) {
            if (isAccept()) {
                System.out.println("当前状态是一半一半,可以休息");
                state = ENERGETIC_STATE;
            } else {
                System.out.println("当前状态是一半一半,但不休息");
            }
        }
    }

    public void papapa() {
        if (state == XIANZHE_STATE) {
            System.out.println("当前状态是贤者时间,不想papapa!");
        } else if (state == ENERGETIC_STATE) {
            System.out.println("当前状态是能量满满,来一发!");
            state = XIANZHE_STATE;
        } else if (state == HALF_STATE) {
            if (isAccept()) {
                System.out.println("当前状态是一半一半,可以papapa");
                state = XIANZHE_STATE;
            } else {
                System.out.println("当前状态是一半一半,但不papapa");
            }
        }
    }

    private boolean isAccept() {
        return new Random().nextBoolean();
    }
}

小菜在rest和papapa方法中增加了对HALF_STATE状态的判断,多了一层if else逻辑。

你觉得这个程序没问题,就向你的二代朋友们炫耀。

然而,这些朋友纷纷表示不屑。

虽然说不出为什么,但总觉得这么多if else不是很优雅。

炫耀不成反丢脸,你拿起你那把40米的大刀准备杀了这位程序员祭天。

40米的大刀

吓得小菜急忙开始对于重构的思考:

  1. 这个程序主要抽象的是富二代状态的转移,可以尝试使用状态模式
  2. 太多的if else的确不利于代码的维护,而且说不准哪天富二代又需要增加状态。

于是,小菜决定使用状态模式进行尝试。

他先抽象出一个接口,接口描述的是某状态下的富二代,无论处于什么状态的富二代,都有rest和papapa方法,通过State接口进行约束。

public interface State {
    void rest();

    void papapa();
}

接着,小菜新建了三个状态类。

状态类中包含FuErDaiV3属性,因为状态类其实表示的是当前状态下的富二代,rest和papapa方法,还是需要富二代来执行。

// 贤者时间状态
public class XianZheState implements State {
    FuErDaiV3 fuErDaiV3;

    public XianZheState(FuErDaiV3 fuErDaiV3) {
        this.fuErDaiV3 = fuErDaiV3;
    }

    @Override
    public void rest() {
        System.out.println("当前状态是贤者时间,需要休息!");
        fuErDaiV3.setState(fuErDaiV3.getEnergeticState());
    }

    @Override
    public void papapa() {
        System.out.println("当前状态是贤者时间,不想papapa!");
    }
}

// 活力满满状态
public class EnergeticState implements State {
    private FuErDaiV3 fuErDaiV3;

    public EnergeticState(FuErDaiV3 fuErDaiV3) {
        this.fuErDaiV3 = fuErDaiV3;
    }

    @Override
    public void rest() {
        System.out.println("当前状态是能量满满,不需要休息!");
    }

    @Override
    public void papapa() {
        System.out.println("当前状态是能量满满,来一发!");
        fuErDaiV3.setState(fuErDaiV3.getXianZheSate());
    }
}

// 一半一半状态
public class HalfState implements State {
    private FuErDaiV3 fuErDaiV3;

    public HalfState(FuErDaiV3 fuErDaiV3) {
        this.fuErDaiV3 = fuErDaiV3;
    }

    @Override
    public void rest() {
        if (isAccept()) {
            System.out.println("当前状态是一半一半,可以休息");
            fuErDaiV3.setState(fuErDaiV3.getEnergeticState());
        } else {
            System.out.println("当前状态是一半一半,但不休息");
        }
    }

    @Override
    public void papapa() {
        if (isAccept()) {
            System.out.println("当前状态是一半一半,可以papapa");
            fuErDaiV3.setState(fuErDaiV3.getXianZheSate());
        } else {
            System.out.println("当前状态是一半一半,但不papapa");
        }
    }

    private boolean isAccept() {
        return new Random().nextBoolean();
    }
}

因为状态各不同,所以不同状态类对于rest和papapa方法的处理也不一样。

接下来是重构之后的富二代类FuErDaiV3。

@Data
public class FuErDaiV3 {
    private State xianZheSate;
    private State energeticState;
    private State halfState;
    private State state;

    public FuErDaiV3() {
        xianZheSate = new XianZheState(this);
        energeticState = new EnergeticState(this);
        halfState = new HalfState(this);
        state = xianZheSate;
    }

    public void rest() {
        state.rest();
    }

    public void papapa() {
        state.papapa();
    }
}

富二代类包含三个已知可能会进入的状态,和一个当前状态属性state。

在构造器新建富二代对象时,需要将本身传入状态类中,分别新建出处于贤者时间的高富帅,处于活力满满状态的高富帅和处于一半一半状态的高富帅。

并且将当前状态/初始状态设置成处于贤者时间的高富帅。

当你需要执行rest和papapa方法时,只需直接调用state的对应方法即可。

在使用了状态模式之后,除了判断百分之五十概率处用了if else,其余的条件判断语句都已被去掉。

当富二代需要增加状态时候,只需新建状态类,以及略微修改富二代类即可。

你很满意,于是唱起了hip-hop。

hip-hop

三:再理解

  1. 状态模式中,富二代类包含状态属性,状态类中又包含富二代属性。相互引用,不知道该先写哪个类。
  2. 状态模式可以去掉对于当前状态的判断语句,方便日后的维护。
  3. 使得富二代的代码做到最清晰。
  4. 不能做到无修改扩展,当增加新状态时候,需为富二代类增加状态属性,并修改构造器。此外,还需要确认其他状态类在执行方法时,是否会进入新状态。不过总体而言,比使用状态模式之前还是优雅太多了。
上一篇下一篇

猜你喜欢

热点阅读