• 设计模式-19-State模式(状态模式)


    什么是状态模式

    根据不同状态对同一行为做出不同的处理。就是比如白天和晚上是两个不同状态(时间),吃饭是一个行为,早上喝粥和晚上吃水果是对这个行为的不同处理。

    如果要在代码中实现这个操作,就会有很多的if 和 else ,然后执行不同的处理。

    状态模式呢,就是将状态和行为的处理整合起来,从业务分离出去。比如我们可以把白天和晚上作为两个状态类,里面的方法就是分别实现不同的吃饭处理,然后在上下文中只保留一个当前状态的类。

    示例程序

    示例程序是模拟金库的行为,在金库中有4个基本方法,setClock设置当前时间,changeState改变状态,callSecurityCenter求救,recordLog留言

    Context接口,负责管理状态和联系警报中心的接口。

    public interface Context {
        void setClock(int hour);
        void changeState(State state);
        void callSecurityCenter(String msg);
        void recordLog(String msg);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    SafeFrame类,使用GUI实现警报系统界面的类,可以把它认为就是金库,实现了Context接口。
    并且内部保存了一个状态对象,把金库想要根据不同状态所做出的处理,分散到各个状态的实现中(比如白天给报警中心打电话就是正常通话,晚上打电话就是留言)。

    public class SafeFrame extends Frame implements ActionListener, Context {
    
        private TextField textClock = new TextField(60);    // 显示当前时间
        private TextArea textScreen = new TextArea(10, 60); // 显示报警中心的记录
        private Button buttonUse = new Button("use");
        private Button buttonAlarm = new Button("push");
        private Button buttonPhone = new Button("call");
        private Button buttonExit = new Button("end");
    
        private State state = DayState.getInstance();
    
        // 构造函数
        
        public SafeFrame(String title) throws HeadlessException {
            super(title);
            setBackground(Color.lightGray);
            setLayout(new BorderLayout());
            // 配置textClock
            add(textClock, BorderLayout.NORTH);
            textClock.setEditable(false);
            // 配置textScreen
            add(textScreen, BorderLayout.CENTER);
            textScreen.setEditable(false);
            // 为界面添加按钮
            Panel panel = new Panel();
            panel.add(buttonUse);
            panel.add(buttonAlarm);
            panel.add(buttonPhone);
            panel.add(buttonExit);
            // 配置界面
            add(panel, BorderLayout.SOUTH);
            // 显示
            pack();
            show();
            // 设置监听器
            buttonUse.addActionListener(this);
            buttonAlarm.addActionListener(this);
            buttonPhone.addActionListener(this);
            buttonExit.addActionListener(this);
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(e.toString());
    
            if (e.getSource() == buttonUse) {
                state.doUse(this);
            } else if (e.getSource() == buttonAlarm) {
                state.doAlarm(this);
            } else if (e.getSource() == buttonPhone) {
                state.doPhone(this);
            } else if (e.getSource() == buttonExit) {
                System.exit(0);
            } else {
                System.out.println("?");
            }
        }
        
        @Override
        public void setClock(int hour) {
            String clockstring = "现在时间是:";
            if (hour < 10) {
                clockstring += "0" +  hour + ":00";
            } else {
                clockstring += hour + ":00";
            }
            System.out.println(clockstring);
            textClock.setText(clockstring);
            state.doClock(this, hour);
        }
    
        @Override
        public void changeState(State state) {
            System.out.println("从" + this.state + "状态变为了 " + state + "状态");
            this.state = state;
        }
    
        @Override
        public void callSecurityCenter(String msg) {
            textScreen.append("call! " + msg + "\n");
        }
    
        @Override
        public void recordLog(String msg) {
            textScreen.append("record ... " + msg + "\n");
        }
    }
    
    • 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

    State接口,表示金库状态的接口,在接口中定义了金库行为的对应方法,用以实现不同状态的不同处理。

    public interface State {
        public abstract void doClock(Context context, int hour);
        public abstract void doUse(Context context);
        public abstract void doAlarm(Context context);
        public abstract void doPhone(Context context);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    DayState类,表示白天的状态,单例模式实现,并且会根据时间(hour)帮助调用金库changeState方法,来改变状态。

    public class DayState implements State {
    
        private static DayState singleton = new DayState();
    
        private DayState(){}
    
        public static DayState getInstance() {
            return singleton;
        }
    
        @Override
        public void doClock(Context context, int hour) {
            if (hour < 9 || 17 <= hour) {
                context.changeState(NightState.getInstance());
            }
        }
    
        @Override
        public void doUse(Context context) {
            context.recordLog("使用金库(白天)");
        }
    
        @Override
        public void doAlarm(Context context) {
            context.callSecurityCenter("按下警铃(白天)");
        }
    
        @Override
        public void doPhone(Context context) {
            context.callSecurityCenter("正常通话(白天)");
        }
    
        @Override
        public String toString() {
            return "[ 白天 ]";
        }
    }
    
    • 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

    NightState类,表示晚上的状态,与白天状态一样实现了State接口,但是提供了晚上状态时不同行为的具体实现。

    public class NightState implements State {
    
        private static NightState singleton = new NightState();
    
        private NightState() {}
    
        public static NightState getInstance() {
            return singleton;
        }
    
        @Override
        public void doClock(Context context, int hour) {
            if (hour >= 9 && 17 > hour) {
                context.changeState(DayState.getInstance());
            }
        }
    
        @Override
        public void doUse(Context context) {
            context.callSecurityCenter("紧急:晚上使用金库!");
        }
    
        @Override
        public void doAlarm(Context context) {
            context.callSecurityCenter("按下警铃(晚上)");
        }
    
        @Override
        public void doPhone(Context context) {
            context.recordLog("晚上的通话录音");
        }
    
        @Override
        public String toString() {
            return "[ 晚上 ]";
        }
    }
    
    • 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

    为什么要用状态模式

    1. 分而治之,在State模式中,我们用类来表示状态,并为每一种具体的状态都定义一个相应的类。这样问题就被分解了,开发人员可以在编写一个ConcreteState角色的代码的同时,在头脑中考虑其他的类。
    2. 依赖于状态的处理
    3. 不会自相矛盾,如果不使用State模式,我们需要使用多个变量的值的集合来表示系统的状态,而变量的值之间可能会互相矛盾。
    4. 易于增加新的状态。

    在这里插入图片描述

  • 相关阅读:
    .gitignore语法及配置问题
    FISCO-BCOS添加新节点
    RPC 框架设计 四、Netty高级应用
    java实现一个简单的监听器
    css--内外边距、 盒子模型、位置、浮动
    【华为OD机试真题 python】任务最优调度【2022 Q4 | 200分】
    『现学现忘』Git基础 — 1、版本控制系统介绍
    消息队列中,如何保证消息的顺序性?
    linux之perf(4)stat统计
    简约工作计划微立体PPT模板
  • 原文地址:https://blog.csdn.net/weixin_44030848/article/details/126437491