美文网首页
设计模式——状态模式

设计模式——状态模式

作者: 9dfaf364d57f | 来源:发表于2018-06-07 23:48 被阅读14次

    前言

    设计模式是指导一个程序猿以更好的姿态处理一些问题,而不再像刚学编程的我们,只会使用if-else分支语句,或是使用硬干的骚操作完成需求。不使用设计模式,一来是代码逻辑会越来越晦涩难懂(到了某天你会发现自己也没办法看清楚所有逻辑),二来是代码维护成本越来越高(你的加班时间会越来越长),三来是可以装bility。基于这么多的好处,小盆友和大家一起讨论和分享下设计模式,让自己不再是一个坐在电脑前敲代码的码农。

    讲个故事——“古怪的电梯”

    老样子,讲设计模式前,先来个场景(讲不出故事了。。只能尬聊了😩)



    设想一下,用程序来表达一个电梯的普通运转流程:关门->运行->停止->开门。这是最为正常的使用流程,乘坐电梯的人进入后,电梯关门,然后电梯运行将人送至目的层数,停止,然后开门。
    但是有个问题就是,这只是一般流程,乘坐电梯的人也可以玩弄电梯:开门->关门->再开门->再关门;或是运行->停止->运行->开门等等等等。
    总之,乘坐电梯的人操作流程有很多,也就是这四种状态是可以相互转换的。但是,细想一下,也不是很周全,因为正常情况下,开门的状态下是不能运行的,运行状态下不能开门,也就是说,四种状态中,有些状态不能转换。我们就这四种状态的转换画一个表格:

    开门open 运行run 停止stop 关门close
    正开着门 ✖️ ✖️ ✖️ ☑️
    运行中 ✖️ ✖️ ☑️ ✖️
    停止 ☑️ ☑️ ✖️ ✖️
    已经关门 ☑️ ☑️ ☑️ ✖️

    看到这张表格,想必大家此时心中已经有千万种实现方式,有些小伙伴可能会用到大量的 switch-case 或 if-else的分支语句进行区分状态的转换流程,这里就不给出此类代码(请允许我的懒惰🤔️),只是略微吐槽一下,如果用了大量的分支语句,弊端是显而易见的:

    1. 扩展性差,当增加一种状态,需要改动的地方多
    2. 逻辑耦合,不易维护;
    3. 不能装逼!!!!

    有经验的小伙伴,很快会想到使用状态模式。在这种行为随状态改变而改变的情况,可以考虑使用“状态模式”。先给出状态模式的类图

    状态模式
    可以看出,状态主要由三部分组成:
    1. State:状态的抽象类
    2. ConcreteState:状态的具体实现类,主要负责实现当前状态的逻辑实现,还有过度到其他状态的逻辑控制(如果不能过度就空实现)
    3. Context:环境角色,负责管理状态

    接下来,我们对上面的场景进行映射


    可以看到,映射的类图其实没有多大的变动,主要的状态转变逻辑会在各自的具体状态实现类中,例如:OpenState转变为CloseState,是由OpenState来维护(将Context的当前状态进行切换),并且打开的逻辑是由OpenState来实现。而Context用于管理调用各自具体的状态。可能现在你有些凌乱,我们可以先进入编码时刻,结合具体代码来理解这段话。

    编码时刻

    先看看抽象状态类,主要是规范所有具体状态,而且持有一个状态管理

    public abstract class LiftState {
        protected Context context;
        public void setContext(Context context) {
            this.context = context;
        }
    
        public abstract void open();
        public abstract void close();
        public abstract void run();
        public abstract void stop();
    
    }
    

    接下来是四个具体的状态

    public class OpenState extends LiftState {
        @Override
        public void open() {
           System.out.println("电梯门开启...");
        }
    
        @Override
        public void close() {
            super.context.setLiftState(Context.CLOSE_STATE);
            super.context.close();
        }
    
        @Override
        public void run() {  //空实现,因为当前状态无法转到run }
    
        @Override
        public void stop() { //空实现,因为当前状态无法转到stop }
    }
    
    public class RunState extends LiftState {
        @Override
        public void open() { //空实现,因为当前状态无法转到open}
    
        @Override
        public void close() { //空实现,因为当前状态无法转到close }
    
        @Override
        public void run() {
            System.out.println("电梯上下运行...");
        }
    
        @Override
        public void stop() {
            super.context.setLiftState(Context.STOP_STATE);
            super.context.stop();
        }
    }
    
    public class StopState extends LiftState {
        @Override
        public void open() {
            super.context.setLiftState(Context.OPEN_STATE);
            super.context.open();
        }
    
        @Override
        public void close() {  //空实现,因为当前状态无法转到close  }
    
        @Override
        public void run() {
            super.context.setLiftState(Context.RUN_STATE);
            super.context.run();
        }
    
        @Override
        public void stop() {
            System.out.println("电梯停止了...");
        }
    }
    
    public class CloseState extends LiftState {
        @Override
        public void open() {
            super.context.setLiftState(Context.OPEN_STATE);
            super.context.open();
        }
    
        @Override
        public void close() {
            System.out.println("电梯门关闭...");
        }
    
        @Override
        public void run() {
            super.context.setLiftState(Context.RUN_STATE);
            super.context.run();
        }
    
        @Override
        public void stop() {
            super.context.setLiftState(Context.STOP_STATE);
            super.context.stop();
        }
    }
    

    状态管理类

    public class Context {
        public final static OpenState OPEN_STATE = new OpenState();
        public final static CloseState CLOSE_STATE = new CloseState();
        public final static RunState RUN_STATE = new RunState();
        public final static StopState STOP_STATE = new StopState();
    
        private LiftState liftState;
    
        public LiftState getLiftState() {
            return liftState;
        }
    
        public void setLiftState(LiftState liftState) {
            this.liftState = liftState;
            this.liftState.setContext(this);
        }
    
        public void open() {
            this.liftState.open();
        }
    
        public void close() {
            this.liftState.close();
        }
    
        public void run() {
            this.liftState.run();
        }
    
        public void stop() {
            this.liftState.stop();
        }
    }
    

    最后是调用代码:

    public class Main {
        public static void main(String[] args) {
            Context context = new Context();
            context.setLiftState(Context.CLOSE_STATE);
            context.open();
            context.close();
            context.run();
            context.stop();
        }
    }
    
    运行结果

    总结

    在这里使用状态模式的好处是,让各自的状态逻辑分成不同的类,维护起来清晰。而且扩展方便,只需要添加一个继承LiftState的类,在Context中增加这个状态即可使用。但是,这里也就暴露了一个状态模式的缺点,当状态较多时,具体的状态类会非常多。所以建议状态最好不超过5个,但只是建议,毕竟程序是死的,人是活的。希望这篇文章,能给你一些启发,减少一些些分支语句,让代码优雅起来。

    相关文章

      网友评论

          本文标题:设计模式——状态模式

          本文链接:https://www.haomeiwen.com/subject/oxpnsftx.html