• 设计模式之禅——状态模式


    我们每一个人都乘过电梯。电梯的动作:开门、关门、执行、停止。

    如今我们用程序来实现一个电梯的动作,先看类图设计,如图所看到的

    这里写图片描写叙述

    如今看一下代码

    public interface ILift {
        //开启电梯
        public void open();
        //关闭电梯
        public void close();
        //能执行。能上能下
        public void run();
        //电梯还要能停下来i
        public void stop();
    }
    
    public class Lift implements ILift {
        @Override
        public void open() {
            System.out.println("电梯门开启...");
        }
    
        @Override
        public void close() {
            System.out.println("电梯门关闭...");
        }
    
        @Override
        public void run() {
            System.out.println("电梯上下执行起来...");
        }
    
        @Override
        public void stop() {
            System.out.println("电梯停止了...");
        }
    }
    
    public class Client {
        public static void main(String[] args) {
            ILift lift = new Lift();
            //首先是电梯门打开,人进去
            lift.open();
            //r然后电梯门关闭
            lift.close();
            //电梯開始执行起来,向上或者向下
            lift.run();
            //最后到达目的地,电梯停下来
            lift.stop();
        }
    }
    /**Output
    电梯门开启...
    电梯门关闭...
    电梯上下执行起来...
    电梯停止了...
    */

    代码很easy,可是这个程序是有问题的。

    ————————————————————
    1、电梯门能够打开,但不是随时都能够打开,电梯执行的时候不能突然开门。

    2、电梯也不会出现停止了可是不开门的情况。
    ————————————————————

    所以我们能够看出。电梯的这四个动作的执行都有前置条件,详细点说就是在特定状态下才干做做特定的事。

    接下来我们分析一下电梯有哪些特定的状态

    • 敞门状态
      按了电梯上下button,电梯门开,这中间大概有10秒的时间。那就是敞门状态。

      在这个状态下电梯仅仅能做的动作就是关门动作。

    • 闭门状态
      电梯门关闭了。在这个状态下。能够进行的动作是:开门(我不想坐电梯了)、停止(忘记按路层号了)、执行。

    • 执行状态
      电梯正在跑,上下窜,在这个状态下,电梯能做的是停止
    • 停止状态
      电梯停止不动。在这个状态下,电梯有两个可选动作:继续执行和开门动作。

    如今又一次进行设计,类图例如以下:
    这里写图片描写叙述

    —————————————————————————————
    在接口中加入了标识变量。类LIft中的open、close等方法带有了逻辑推断。


    —————————————————————————————

    代码例如以下:

    public interface ILift {
        //电梯的4个状态
        final static int OPENING_STATE = 1;
        final static int CLOSING_STATE = 2;
        final static int RUNNING_STATE = 3;
        final static int STOPPING_STATE = 4;
        //开启电梯
        public void open();
        //关闭电梯
        public void close();
        //能执行,能上能下
        public void run();
        //电梯还要能停下来i
        public void stop();
    }
    
    public class Lift implements ILift {
        private int state;
        public void setState(int state) {
            this.state = state;
        }
        @Override
        public void open() {
           //电梯在什么状态下才干开启
            switch (this.state) {
                case OPENING_STATE:
                    break;
                case CLOSING_STATE:
                    this.openWithoutLogic();
                    this.setState(OPENING_STATE);
                    break;
                case RUNNING_STATE:
                    break;
                case STOPPING_STATE:
                    this.openWithoutLogic();
                    this.setState(OPENING_STATE);
                    break;
            }
        }
    
        @Override
        public void close() {
            //电梯在什么状态下才干关闭
            switch (this.state) {
                case OPENING_STATE:
                    this.closeWithoutLogic();
                    this.setState(CLOSING_STATE);
                    break;
                case CLOSING_STATE:
                    break;
                case RUNNING_STATE:
                    break;
                case STOPPING_STATE:
                    break;
            }
        }
    
        @Override
        public void run() {
            switch (this.state) {
                case OPENING_STATE:
                    break;
                case CLOSING_STATE:
                    this.runWithoutLogic();
                    this.setState(RUNNING_STATE);
                    break;
                case RUNNING_STATE:
                    break;
                case STOPPING_STATE:
                    this.runWithoutLogic();
                    this.setState(RUNNING_STATE);
                    break;
            }
        }
    
        @Override
        public void stop() {
            switch (this.state) {
                case OPENING_STATE:
                    break;
                case CLOSING_STATE:
                    this.stopWithoutLogic();
                    this.setState(STOPPING_STATE);
                    break;
                case RUNNING_STATE:
                    this.runWithoutLogic();
                    this.setState(STOPPING_STATE);
                    break;
                case STOPPING_STATE:
                    break;
            }
        }
        public void closeWithoutLogic(){
            System.out.println("电梯门关闭...");
        }
        public void openWithoutLogic(){
            System.out.println("电梯门开启...");
        }
        public void runWithoutLogic(){
            System.out.println("电梯上下执行起来...");
        }
        public void stopWithoutLogic(){
            System.out.println("电梯停止了...");
        }
    }
    
    public class Client {
        public static void main(String[] args) {
            ILift lift = new Lift();
            lift.setState(OPENING_STATE);
            //首先是电梯门打开,人进去
            lift.open();
            //r然后电梯门关闭
            lift.close();
            //电梯開始执行起来,向上或者向下
            lift.run();
            //最后到达目的地,电梯停下来
            lift.stop();
        }
    }
    /**Output
    电梯门开启...
    电梯门关闭...
    电梯上下执行起来...
    电梯停止了...
    */

    好了,改过的代码已经实现了简单的逻辑控制。

    可是它好像还是有不少问题。
    ————————————————————————————

    • 电梯实现类Lift有点长
      -长的原因是由于我们在程序中使用了大量的switch…case这种推断(if…else也是一样)

    • 拓展类很差,电梯还有两个状态。通电状态。断电状态,你要是在程序添加这两个方法,你看看Open()、Close()、Run()、Stop()这四个方法都要添加推断条件,也就是说在switch推断体中还要添加case项、这与开闭原则相违背。

    • 很规状态无法实现
      考虑电梯故障等情况就更麻烦了…

    —————————————————————————————
    我们已经发现程序中有以上问题,我们怎么来改动呢?
    刚刚我们是从电梯的方法以及这些方法执行的条件去分析,如今我们换个角度来看问题。

    我们来想。电梯在具有这些状态的时候能够做什么实行,也就是说电梯在处于某个详细状态时,我们来思考 这个状态是由什么动作出发触发的而产生的,以及在这个状态下电梯还能做什么事情。

    比如,电器在停止状态时,我们来思考两个问题:
    —————————————————————————————
    - 停止状态是怎么来的,那当然是由于电梯执行了stop方法而来的。
    -
    - 在停止状态下。电梯还能做什么动作?继续执行?开门?当然都能够了

    —————————————————————————————
    我们再来分析其它三个状态,也都是一样的结果。我们仅仅要实现电梯在一个状态下的两个任务模型就能够了:这个状态是怎样产生的。以及在这个状态下还能做什么其它动作(也就是这个状态怎么过度到其它状态),既然我们以状态作为參考模型。那我们先定义电梯的状态接口,类图例如以下
    这里写图片描写叙述

    代码例如以下

    public abstract class LiftState {
        //定义一个环境角色,也就是封装状态的变化引起的功能变化
        protected static Context context = new 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 Context {
        //定义出全部的电梯状态
        public final static OpenningState openningState =
                new OpenningState();
        public final static ClosingState closingState =
                new ClosingState();
        public final static RunningState runningState =
                new RunningState();
        public final static StoppingState stoppingState =
                new StoppingState();
        //定义一个当前的电梯状态
        private LiftState liftState;
        public LiftState getLiftState() {
            return liftState;
        }
    
        public void setLiftState(LiftState liftState) {
            this.liftState = liftState;
           // this.liftState.stop();
        }
        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 ClosingState extends LiftState {
        //电梯门关闭,这是关闭状态要实现的动作
        @Override
        public void close() {
            System.out.println("电梯门关闭...");
        }
        //电梯门关了再打开
        @Override
        public void open() {
            super.context.setLiftState(Context.openningState);
            super.context.getLiftState().open();
        }
    
        @Override
        public void run() {
            super.context.setLiftState(Context.runningState);
            super.context.getLiftState().run();
        }
    
        @Override
        public void stop() {
            super.context.setLiftState(Context.stoppingState);
            super.context.getLiftState().stop();
        }
    }
    
    public class OpenningState extends LiftState {
        @Override
        public void open() {
            System.out.println("电梯门开启...");
        }
    
        @Override
        public void close() {
            //状态改动
            super.context.setLiftState(Context.closingState);
            super.context.getLiftState().close();
        }
        @Override
        public void run() {
    
        }
    
        @Override
        public void stop() {
    
        }
    }
    
    public class RunningState extends LiftState {
        @Override
        public void open() {
    
        }
    
        @Override
        public void close() {
    
        }
    
        @Override
        public void run() {
            System.out.println("电梯门上下执行...");
        }
    
        @Override
        public void stop() {
            super.context.setLiftState(Context.stoppingState);
            super.context.getLiftState().stop();
        }
    }
    
    public class StoppingState extends LiftState {
        @Override
        public void open() {
            super.context.setLiftState(Context.openningState);
            super.context.getLiftState().open();
        }
    
        @Override
        public void close() {
    
        }
    
        @Override
        public void run() {
            super.context.setLiftState(Context.runningState);
            super.context.getLiftState().run();
        }
        //电梯停止是怎么发生的
        @Override
        public void stop() {
            System.out.println("电梯停止了...");
        }
    }
    

    把逻辑放到没一个状态内,然后用context整合,做到加入状态的时候也不须要改动原来的代码。

    状态模式的定义:当一个对象内在状态改变时同意其改变行为,这个对象 看起来像改变了其类。

    状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象相应的类发生了改变一样。

    状态模式的角色主要有三个:抽象状态角色,详细状态 角色,环境角色

    状态模式的长处:

    • 结构清晰。避免了过多的switch…case或者if…else语句的使用
    • 遵循设计原则,许多地体现了开闭原则和单一职责原则,每一个状态都是一个子类。
    • 封装性比較好

    状态模式的缺点:
    - 类膨胀

    状态模式的使用场景

    • 行为随状态改变而改变
    • 条件、分支推断语句的替代者

    注意事项

    • 子类最好不要超过5个
  • 相关阅读:
    论分治与归并思想
    关于缩短cin时间的方法
    【lower_bound、upperbound讲解、二分查找、最长上升子序列(LIS)模版】
    getDomain(url)-我的JavaScript函数库-mazey.js
    jQuery-PHP跨域请求数据
    ASP-Server.Transfer-Response.Redirect
    jQuery获取相邻标签的值
    分界线<hr/>
    jQuery获取input复选框的值
    Bootstrap支持的JavaScript插件
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8571771.html
Copyright © 2020-2023  润新知