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逻辑,使代码更容易阅读和维护
    • 将状态引起的变化独立封装在各自的实现中,所以它符合开闭原则

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

  • 相关阅读:
    Ext JS学习第三天 我们所熟悉的javascript(二)
    Ext JS学习第二天 我们所熟悉的javascript(一)
    Ext JS学习第十七天 事件机制event(二)
    Ext JS学习第十六天 事件机制event(一)
    Ext JS学习第十五天 Ext基础之 Ext.DomQuery
    Ext JS学习第十四天 Ext基础之 Ext.DomHelper
    Ext JS学习第十三天 Ext基础之 Ext.Element
    Ext JS学习第十天 Ext基础之 扩展原生的javascript对象(二)
    针对错误 “服务器提交了协议冲突. Section=ResponseHeader Detail=CR 后面必须是 LF” 的原因分析
    C# 使用HttpWebRequest通过PHP接口 上传文件
  • 原文地址:https://www.cnblogs.com/zanpen2000/p/7668236.html
Copyright © 2011-2022 走看看