• Java 设计模式——状态模式


    1.概述

    【例】通过按钮来控制一个电梯的状态,电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。其类图如下:
    在这里插入图片描述
    具体实现代码如下:

    ILift.java

    public interface ILift {
    
        //定义四个电梯状态的常量
        int OPENING_STATE = 1;
        int CLOSING_STATE = 2;
        int RUNNING_STATE = 3;
        int STOPPING_STATE = 4;
    
        //设置电梯状态的功能
        void setState(int state);
    
        //电梯操作功能
        void open();
        void close();
        void run();
        void stop();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Lift.java

    //电梯类(ILift 接口的实现类)
    public class Lift implements ILift {
    
        //声明一个记录当前电梯的状态
        private int state;
    
        public void setState(int state) {
            this.state = state;
        }
    
        public void open() {
            switch (state) { //当前电梯状态
                case OPENING_STATE :
                    //什么事都不做
                    break;
                case CLOSING_STATE :
                    System.out.println("电梯打开了...");
                    //设置当前电梯状态为开启状态
                    setState(OPENING_STATE);
                    break;
                case STOPPING_STATE :
                    System.out.println("电梯打开了...");
                    //设置当前电梯状态为开启状态
                    setState(OPENING_STATE);
                    break;
                case RUNNING_STATE :
                    //什么事都不做
                    break;
            }
        }
    
        public void close() {
            switch (this.state) {
                case OPENING_STATE:
                    System.out.println("电梯关门了...");	//只有开门状态可以关闭电梯门,可以对应电梯状态表来看
                    this.setState(CLOSING_STATE);		//关门之后电梯就是关闭状态了
                    break;
                case CLOSING_STATE:
                    //do nothing 	//已经是关门状态,不能关门
                    break;
                case RUNNING_STATE:
                    //do nothing 	//运行时电梯门是关着的,不能关门
                    break;
                case STOPPING_STATE:
                    //do nothing 	//停止时电梯也是关着的,不能关门
                    break;
            }
        }
    
        public void run() {
            switch (this.state) {
                case OPENING_STATE:	//电梯不能开着门就走
                    //do nothing
                    break;
                case CLOSING_STATE:	//门关了,可以运行了
                    System.out.println("电梯开始运行了。。。");
                    this.setState(RUNNING_STATE);//现在是运行状态
                    break;
                case RUNNING_STATE:
                    //do nothing 已经是运行状态了
                    break;
                case STOPPING_STATE:
                    System.out.println("电梯开始运行了。。。");
                    this.setState(RUNNING_STATE);
                    break;
            }
        }
    
        public void stop() {
            switch (this.state) {
                case OPENING_STATE: 	//开门的电梯已经是是停止的了(正常情况下)
                    //do nothing
                    break;
                case CLOSING_STATE:		//关门时才可以停止
                    System.out.println("电梯停止了。。。");
                    this.setState(STOPPING_STATE);
                    break;
                case RUNNING_STATE:		//运行时当然可以停止了
                    System.out.println("电梯停止了。。。");
                    this.setState(STOPPING_STATE);
                    break;
                case STOPPING_STATE:
                    //do nothing
                    break;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    Client.java

    public class Client {
        public static void main(String[] args) {
            //创建电梯对象
            Lift lift = new Lift();
    
            //设置当前电梯的状态
            lift.setState(ILift.OPENING_STATE);
    
            //打开
            lift.open();
            lift.close();
            lift.run();
            lift.stop();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    问题分析:

    • 使用了大量的 switch…case 这样的判断(换成 if…else 也是一样),使程序的可读性变差。
    • 扩展性很差,如果新加了断电的状态,则需要修改上面的判断逻辑。

    (2)状态模式 (State Pattern) 是一种行为型设计模式,它允许在一个对象内部状态改变时改变它的行为。状态模式将状态封装成独立的类,并将动作委托给代表当前状态的对象,以此实现状态转换时的行为变化。使用状态模式,我们可以将状态转换规则封装在不同状态类中,让上下文对象从复杂的状态转换逻辑中解耦出来,同时利用多态特性让状态转换具有扩展性和灵活性。

    2.结构

    状态模式包含以下主要角色:

    • 上下文 (Context) 角色:是一个包含状态的对象,它在运行时根据不同的状态改变其行为。上下文对象持有一个对当前状态的引用,并将具体的操作委托给当前状态对象处理。
    • 抽象状态 (Abstract State) 角色:定义了一个公共的接口,用于封装不同状态的行为。可以是一个抽象类或接口,并声明了在不同状态下可能发生的方法。
    • 具体状态 (Concrete State) 角色:实现了抽象状态接口,并定义了在对应状态下具体的行为。每个具体状态类都封装了一种特定状态的行为。

    3.案例实现

    对上述电梯的案例使用状态模式进行改进。类图如下:
    在这里插入图片描述
    具体实现代码如下:

    3.1.抽象状态类

    LiftState.java()

    //抽象状态类
    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3.2.具体状态类

    OpeningState.java

    //电梯开启状态类
    public class OpeningState extends LiftState {
        //当前状态要执行的方法
        public void open() {
            System.out.println("电梯开启。。。");
        }
    
        public void close() {
            //修改状态
            super.context.setLiftState(Context.CLOSING_STATE);
            //调用当前状态中的context中的close方法
            super.context.close();
        }
    
        public void run() {
            //什么都不做
        }
    
        public void stop() {
            //什么都不做
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    ClosingState.java

    //电梯关闭状态类
    public class ClosingState extends LiftState {
        @Override
        //电梯门关闭,这是关闭状态要实现的动作
        public void close() {
            System.out.println("电梯门关闭...");
        }
    
        //电梯门关了再打开,逗你玩呢,那这个允许呀
        @Override
        public void open() {
            super.context.setLiftState(Context.OPENING_STATE);
            super.context.open();
        }
    
        //电梯门关了就跑,这是再正常不过了
        @Override
        public void run() {
            super.context.setLiftState(Context.RUNNING_STATE);
            super.context.run();
        }
    
        //电梯门关着,我就不按楼层
        @Override
        public void stop() {
            super.context.setLiftState(Context.STOPPING_STATE);
            super.context.stop();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    RunningState.java

    //电梯运行状态类
    public class RunningState extends LiftState {
        //运行的时候开电梯门?你疯了!电梯不会给你开的
        @Override
        public void open() {
            //do nothing
        }
    
        //电梯门关闭?这是肯定了
        @Override
        public void close() {//虽然可以关门,但这个动作不归我执行
            //do nothing
        }
    
        //这是在运行状态下要实现的方法
        @Override
        public void run() {
            System.out.println("电梯正在运行...");
        }
    
        //这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了
        @Override
        public void stop() {
            super.context.setLiftState(Context.STOPPING_STATE);
            super.context.stop();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    StoppingState.java

    //电梯停止状态类
    public class StoppingState extends LiftState {
        //停止状态,开门,那是要的!
        @Override
        public void open() {
            //状态修改
            super.context.setLiftState(Context.OPENING_STATE);
            //动作委托为CloseState来执行,也就是委托给了 ClosingState 子类执行这个动作
            super.context.getLiftState().open();
        }
    
        @Override
        public void close() {//虽然可以关门,但这个动作不归我执行
            //状态修改
            super.context.setLiftState(Context.CLOSING_STATE);
            //动作委托为 CloseState 来执行,也就是委托给了 ClosingState 子类执行这个动作
            super.context.getLiftState().close();
        }
    
        //停止状态再跑起来,正常的很
        @Override
        public void run() {
            //状态修改
            super.context.setLiftState(Context.RUNNING_STATE);
            //动作委托为 CloseState 来执行,也就是委托给了 ClosingState 子类执行这个动作
            super.context.getLiftState().run();
        }
    
        //停止状态是怎么发生的呢?当然是停止方法执行了
        @Override
        public void stop() {
            System.out.println("电梯停止了...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    3.3.上下文类

    Context.java

    //环境角色类
    public class Context {
    
        //定义对应状态对象的常量
        public final static OpeningState OPENING_STATE = new OpeningState();
        public final static ClosingState CLOSING_STATE = new ClosingState();
        public final static RunningState RUNNING_STATE = new RunningState();
        public final static StoppingState STOPPING_STATE = new StoppingState();
    
        //定义一个当前电梯状态变量
        private LiftState liftState;
    
        public LiftState getLiftState() {
            return liftState;
        }
    
        //设置当前状态对象
        public void setLiftState(LiftState liftState) {
            this.liftState = liftState;
            //设置当前状态对象中的 Context 对象
            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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    3.4.测试

    Client.java

    public class Client {
        public static void main(String[] args) {
            //创建环境角色对象
            Context context = new Context();
            //设置当前电梯装填
            context.setLiftState(new ClosingState());
    
            context.open();
            context.run();
            context.close();
            context.stop();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.优缺点

    • 优点:
      • 将状态封装成独立的类,使得状态转换的逻辑更加清晰,避免了大量的条件判断语句。
      • 将状态和状态切换的行为分离,使得状态转换的代码可扩展和可维护。增加新的状态类或修改现有的状态类时,对上下文对象和其他状态类都没有影响。
      • 增强了代码的可读性和可维护性。每个状态都有自己的类,使得代码具备了良好的结构化,并且易于理解和修改。
      • 通过引入多态性,可以方便地增加新的状态类,而不需要修改现有的代码。
    • 缺点:
      • 如果状态类很多,可能会导致类的数量庞大,增加了代码的复杂性。
      • 状态类的创建和销毁需要维护和管理,可能会增加系统的开销。
      • 当状态转换的逻辑较为简单时,使用状态模式可能会显得过于繁琐。

    5.使用场景

    (1)状态模式适用于以下情况:

    • 当一个对象的行为取决于其内部状态,并且在不同状态下需要有不同的行为时,可以使用状态模式。例如,订单状态的改变会导致不同的操作和行为,可以使用状态模式来管理订单的状态转换。
    • 当有大量的条件语句用于根据不同状态执行不同操作时,可以使用状态模式来简化代码并提高可维护性。
    • 当一个对象的行为需要根据多个条件进行判断,并且这些条件随着时间的推移可能会发生变化时,可以使用状态模式。通过将每个条件封装为一个状态类,可以方便地添加、修改或删除状态,而不会对其他部分产生影响。
    • 当需要在运行时动态改变对象的行为时,可以使用状态模式。通过改变对象的状态,可以改变其行为,而不需要修改对象的代码。

    (2)总之,状态模式适用于需要根据对象的内部状态来改变其行为,并且希望将状态转换逻辑封装在独立的状态类中的情况。它提供了一种清晰、灵活和可扩展的方式来管理对象的状态和行为。

  • 相关阅读:
    docker介绍及入门举例
    unity 双击物体让其显示,再次双击让其隐藏
    1000元订金?华为折叠屏手机MateX5今日开始预订,售价尚未公布
    17.重定向(redirect)和请求转发(forward)
    FAQ docker运行tomcat提示找不到文件
    android_adb pm和am
    云存储架构——打造安全的企业级数据流转平台技术方案
    TortoiseGit使用教程
    【Godot测试】【在Godot中添加VRM模型和VMD动画并播放】
    蓝桥杯算法记录
  • 原文地址:https://blog.csdn.net/weixin_43004044/article/details/134242805