zoukankan      html  css  js  c++  java
  • 状态模式

    摘要

    本文以 Java 和 C# 示例说明状态模式的概念和应用场景。

    定义

    状态模式(state pattern)允许对象在内部状态改变时改变它的行为, 对象看起来好像修改了它的类。

    Alt Text

    解读

    • 行为根据状态改变,不同的状态有不同的行为

    场景举例

    • 自动贩卖机
    • 家用电表
    • ...

    状态模式和策略模式

    • 策略模式围绕可以互换的算法进行业务操作
    • 状态模式通过改变对象的内部状态来帮助对象控制自己的行为
    • 状态模式将各个状态所对应的操作分离开来,即对于不同的状态,由不同的子类实现具体操作,不同状态的切换由子类实现,当发现传入参数不是自己这个状态所对应的参数,则自己给Context类切换状态;而策略模式是直接依赖注入到Context类的参数进行选择策略,不存在切换状态(取自知乎网友的回答)

    下面用代码示例进行说明:

    代码示例

    Java代码

    
    // 状态接口
    public interface ManState {
        String NextBehavior();
    }
    
    // 状态-早上醒来
    public class Wakeup implements ManState {
        @Override
        public String NextBehavior() {
            return "正在起床";
        }
    }
    
    // 状态-吃早餐
    public class Eatting implements ManState {
        @Override
        public String NextBehavior() {
            return "正在吃早饭";
        }
    }
    
    // 状态-上班
    public class Working implements ManState{
        @Override
        public String NextBehavior() {
            return "正在工作";
        }
    }
    
    // 状态-睡觉
    public class Sleeping implements ManState{
        @Override
        public String NextBehavior() {
            return "正在睡觉";
        }
    }
    
    // 上下文 - Man
    public class Man {
    
        private ManState state;
    
        public Man(){
            state = null;
        }
    
        // 设置状态
        public void setState(ManState state){
            this.state = state;
        }
    
        // 获取状态
        public ManState getState(){
            return state;
        }
    }
    
    public static void main(String[] args) throws Exception {
            
            //定义上下文
            Man man = new Man();
    
            // 醒了
            ManState state = new Wakeup();
    
            this.man.setState(state);
            this.man.getState().NextBehavior();
    
            // 吃饭
            state = new Eatting();
    
            this.man.setState(state);
            this.man.getState().NextBehavior();
    
            // 上班
            state = new Working();
    
            this.man.setState(state);
            this.man.getState().NextBehavior();
    
            //睡觉
            state = new Sleeping();
    
            this.man.setState(state);
            this.man.getState().NextBehavior();
    }
    
    

    上面的例子,以员工为例,说明一天中做的事情,其状态是在外部进行控制的,事实上,根据需求,也可以在内部实现状态的转换,这就需要将上下文注入到具体状态的实现中,以实现一个类似指针的效果。

    public interface EmpState {
        void DoSomething(Employee emp);
    }
    
    public class Wakeup implements EmpState {
        @Override
        public void DoSomething(Employee emp) {
            System.out.println("起床了");
            emp.SetState(new Eatting());
        }
    }
    
    public class Eatting implements  EmpState {
        @Override
        public void DoSomething(Employee emp) {
            System.out.println("吃早饭呢");
            emp.SetState(new Working());
        }
    }
    
    public class Working implements EmpState {
        @Override
        public void DoSomething(Employee emp) {
            System.out.println("正在工作");
            emp.SetState(new Sleeping());
        }
    }
    
    public class Sleeping implements EmpState {
        @Override
        public void DoSomething(Employee emp) {
            System.out.println("正在睡觉");
            System.out.println("没有其他状态了");
        }
    }
    
    public class JavaStateDemo {
    
        public static void main(String[] args) {
    
            EmpState state = new Wakeup();
    
            //需要一个初始状态
            Employee emp = new Employee(state);
    
            emp.Go();
            emp.Go();
            emp.Go();
            emp.Go();
        }
    }
    
    

    这个例子从内部改变Employee的状态,以实现不同的行为。

    员工请假/工作天数计算示例

    设想有以下场景:需求方连续请假和非连续请假的处理措施是不同的,需要清楚的知道员工每个月的工作和请假情况,我们实现如下的状态操作模型

    using System;
    
    namespace StatePatternDemo
    {
        // 抽象状态接口
        public interface IWorkState
        {
            void HandleWorkState(Employee emp);
        }
    
        // 请假
        public class Leaving:IWorkState
        {
            public void HandleWorkState(Employee emp)
            {
                System.Console.WriteLine($"{emp.Name} 请假 {emp.LeavingDays} 天");
                System.Console.WriteLine($"{emp.Name} 上次请假日期是 {emp.LastLeavingDate} ");
            }
        }
    
        // 工作
        public class Working:IWorkState
        {
            public void HandleWorkState(Employee emp)
            {
                System.Console.WriteLine($"{emp.Name} 正常上班中...");
                System.Console.WriteLine($"{emp.Name} 已连续工作 {Datetime.Now.Day - LastLeavingDate} 天");
            }
    
        }
    
        // 上下文
        public class Employee
        {
            // 职员姓名(标识)
            public string Name { get; set; }
    
            public double LeavingDays { get; set; } // 本次请假天数
            public Datetime LastLeavingDate { get; set; } // 上次请假日期
            public double TotalLeavingDays { get; set; } // 共请假天数
    
            // 工作天数
            public double WorkingDays { get; set; }
    
            //公休天数
            private int restDays { get; set;}
    
            private IWorkState WorkState;
    
            public Employee(IWorkState workState)
            {
                this.WorkState = workState;
            }
    
            public void SetState(IWorkState workState)
            {
                this.WorkState = workState;
            }
    
            /// <code>
            /// 上班签到或请假导致的状态改变
            /// </code>
            public void WorkingStateChanging(Employee emp)
            {
                this.WorkState.HandleWorkState(Employee emp);
            }
        }
    }
    
    

    如上代码中,我把上下文类变成了Employee对象,代表一个职员,这个职员有两种不同的状态,其中请假状态还可以细分为连续请假和非连续请假,也就是说,如果代码继续细化,我们还可以在该状态模式中再嵌套一个状态模式,以更清楚的描述需求。

    
    namespace StatePatternDemo
    {
        public interface ILeavingState
        {
            void Leaving(Employee emp);
        }
    
    
        public class NotContinusLeaving:ILeavingState
        {
            public void Leaving(Employee emp)
            {
                //非连续请假的实现逻辑
            }
        }
    
        public class ContinusLeaving:ILeavingState
        {
            public void Leaving(Employee emp)
            {
                //非连续请假的实现逻辑
            }
        }
    
        // 其他逻辑
    }
    

    小结:

    • 状态模式避免了代码中繁复的if/else/switch逻辑,使代码更容易阅读和维护
    • 将状态引起的变化独立封装在各自的实现中,所以它符合开闭原则

    以上就是我对状态模式的理解,认为我理解的有问题的,可以在下面留言,谢谢大家!

  • 相关阅读:
    Delphi实战中讲解FormCreate,FormShow,FormActivate
    delphi Try except on e:Exception do
    Delphi处理数据网格DBGrid的编辑框 获取还没有提交到数据集的字段文本
    delphi dbgrid中如何自动生成序号
    DBDateTimePicker;
    Delphi控件开发浅入深出(八)
    delphi中日期类型TDateTime使用总结
    在DBGrid录数据时,如何判断光标位置是在数据的最左或最右,如果是最左或最右则在按左右光标键时光标跳到上一格或下一格,如果是在数据中
    请问如何按Enter键让DBGrid的光标向右移以及换行?(0分)
    tdbgrid中用enter仿真tab键盘_delphi教程
  • 原文地址:https://www.cnblogs.com/zanpen2000/p/7668236.html
Copyright © 2011-2022 走看看