状态模式----设计模式

状态State,指某事物所处的状况或形态,比如水的三态,零下会变成固态冰,常温会是液态水,100℃会蒸发成气态的水蒸气。

言归正传,依旧老惯例,我们还是用极简主义阴阳二态来做药引子,想必每个人家里都有开关吧,其暴露出两个UI可操作接口(对接你的手指):开,关。很简单吧?

好我们来分析一下,首先得定义一个类吧,就叫它:Switcher好了,对外暴露两个方法:switchOn()以及switchOff(),以便用户调用,OK,开始我们的代码。

 1public class Switcher {
 2    //false代表关,true代表开
 3    private boolean state = false;//初始状态是关
 4
 5    public void switchOn(){
 6        state = !state;
 7        System.out.println("OK...灯亮");
 8    }
 9
10    public void switchOff(){
11        state = !state;
12        System.out.println("OK...灯灭");
13    }
14}

完成了?没问题了?这也太简单了吧?当然说这个没问题是在前端UI壳子设计精妙的前提下,但这并不能代表我们的程序设计没问题。试想如果UI可以重复调用开或者关会出现什么情况?状态乱套了!这个设计是非常不可靠的,我们不能因为表面设计上的完美就忽略了后端代码功能的逻辑正确性,表里不一。这就是为什么我们做应用时不但要做好前端校验(用户体验),更要保证后端校验(功能正确性)不可缺失。

想明白了的话我们继续,现在改一下我们之前的设计,这里一定要加入针对当前状态的条件判断,也就是说,开的状态不能再开,关的状态不能再关!

 1public class Switcher {
 2    //false代表关,true代表开
 3    boolean state = false;//初始状态是关
 4
 5    public void switchOn(){
 6        if(state == false){//当前是关状态
 7            state = true;
 8            System.out.println("OK...灯亮");
 9        }else{//当前是开状态
10            System.out.println("WARN!!!通电状态无需再开");
11        }
12    }
13
14    public void switchOff(){
15        if(state == true){//当前是开状态
16            state = false;
17            System.out.println("OK...灯灭");
18        }else{//当前是关状态
19            System.out.println("WARN!!!断电状态无需再关");
20        }
21    }
22}

我们可以看到这里加入了逻辑判断,如果重复开或者重复关的话是会告警的,当然这里也可以抛异常出去,我们就不搞那么复杂化了。那对于这样的设计没有问题吧?很显然,逻辑上是跑的通的,写个Client类测试一下。

public class Client {

    public static void main(String[] args) {
        Switcher s = new Switcher();
        s.switchOff();//WARN!!!断电状态无需再关
        s.switchOn();//OK...灯亮
        s.switchOff();//OK...灯灭
        s.switchOn();//OK...灯亮
        s.switchOn();//WARN!!!通电状态无需再开
    }

}

So far,不管熊孩子怎么开开关关都不会有问题了。可惜我还是要很遗憾地告诉你,这样的设计仍然是糟糕的。试想,如果状态不止一种,并且状态切换有及其复杂的逻辑,例如,或者汽车的自动挡。

 1public class Car {
 2    //0:Park驻车档,1:Reverse倒退挡,
 3    //2:Neutral空挡,3:Drive前进档。
 4    String state = "P";//初始状态是P档
 5
 6    public void push(){//向上推档杆
 7        switch (state) {
 8        case "P"://驻车档状态
 9            System.out.println("WARN!!!到头了推不动了!");
10            break;
11        case "R"://倒挡状态
12            state = "P";
13            System.out.println("OK...切P档");
14            break;
15        case "N"://空档状态
16            System.out.println("OK...切R档");
17            break;
18        case "D"://前进档状态
19            System.out.println("OK...切N档");
20            break;
21        default:
22            break;
23        }
24    }
25
26    public void pull(){//向下拉档杆
27        //这里省略,逻辑同上类似
28    }
29}

不用多说什么了吧,这个是在作死了,那一大堆逻辑判断写在宿主类里会越来越像蜘蛛网!我们必须想方设法把这个设计给模块化,把状态模块给独立出来!算法策略被抽离出来,这里举一反三,把状态也给抽离出来,好了办法有了,我们忘掉自动挡,继续用我们大道至简的开关例子。

public interface State {
    public void switchOn(Switcher switcher);//开
    public void switchOff(Switcher switcher);//关
}

以上我们首先了定义一个状态State接口,两个方法开与关,注意这里与策略模式不同的是,我们为了与宿主Switcher对接所以把它作为参数传入。然后是开状态与关状态的实现。

 1public class On implements State {
 2    @Override
 3    public void switchOn(Switcher switcher) {
 4        System.out.println("WARN!!!通电状态无需再开");
 5        return;
 6    }
 7
 8    @Override
 9    public void switchOff(Switcher switcher) {
10        switcher.setState(new Off());
11        System.out.println("OK...灯灭");
12    }
13}
 1public class Off implements State {
 2    @Override
 3    public void switchOn(Switcher switcher) {
 4        switcher.setState(new On());
 5        System.out.println("OK...灯亮");
 6    }
 7
 8    @Override
 9    public void switchOff(Switcher switcher) {
10        System.out.println("WARN!!!断电状态无需再关");
11        return;
12    }
13}

显而易见,注意看第10行代码,开状态不能做开行为,只告警并返回,关状态反之亦然。而第4行代码则是合法的行为,所以可以进行状态切换并实施相应行为,也就是说,开状态可关,关状态可开。注意这里是把宿主对象传入进来用于切换其当前状态,亦或是调用宿主的具体功能方法(这里省略用打印输出代替),比如宿主里的一盏灯提供的方法。

至此,一切看起来非常优雅,我们已经成功的将状态从宿主中抽离了,最后再来看宿主开关类是什么样子。

 1public class Switcher {
 2    //开关的初始状态设置为“关”
 3    private State state = new Off();
 4
 5    public State getState() {
 6        return state;
 7    }
 8
 9    public void setState(State state) {
10        this.state = state;
11    }
12
13    public void switchOn(){
14        state.switchOn(this);//这里调用的是当前状态的开方法
15    }
16
17    public void switchOff(){
18        state.switchOff(this);//这里调用的是当前状态的关方法
19    }
20}

甚至我们还可以给里面加一盏灯,像之前我们提到的那样,在State状态接口实现里去调用。

public class Switcher {
    //...之上代码略...
    private Lamp lamp;

    public void lampOn(){
        lamp.on();
    }

    public void lampOff(){
        lamp.off();
    }
}

看明白了吧?是不是很像策略模式?其实它就是策略的一个变种,只不过状态模式会更好的根据当前的状态去实施不同的行为,并且自主切换到另一个正确的状态,开变关,关变开。

猜你喜欢

转载自blog.csdn.net/wang_nian/article/details/108141608