zoukankan      html  css  js  c++  java
  • 二十三种设计模式[20]

    前言

           状态模式,对象行为型模式的一种。在《设计模式 - 可复用的面向对象软件》一书中将之描述为“ 允许一个对象在其内部状态改变时改变它的行为,使对象看起来似乎修改了它的类 ”。

    场景

           我们都坐过火车,火车可以简单的分为“ 开门 ”,“ 关门 ”,“ 运行 ”,“ 停止 ”四个状态。火车在这四个状态下分别可以做不同的事情。比如只有在关门时才行运行、只有在停止时才能开门。

           我们在开发类似的业务时,往往会在“ 火车对象 ”中保持一个属性来标识当前的状态,并且在执行相应操作时去验证这个状态。

    public class Train
    {
        public TrainState State { set; get; }
    
        public void OpenDoor()
        {
            if(this.State == TrainState.OpenDoor)
            {
                Console.WriteLine("火车门已经打开");
            }
            else if(this.State == TrainState.CloseDoor
                    || this.State == TrainState.Stop)
            {
                Console.WriteLine("正在打开火车门");
            }
            else if (this.State == TrainState.Run)
            {
                Console.WriteLine("火车正在运行中,不可打开火车门");
            }
        }
    }
    
    public enum TrainState
    {
        OpenDoor,
        CloseDoor,
        Run,
        Stop
    }

           在这种情况下,往往需要通过if-else或switch-case语句来做判断。大量if-else语句的使用使得代码的可阅读性很差,扩展起来的麻烦的很,并且使用属性来标识对象的状态也不够明确。为了避免这种情况,可使用状态模式来明确对象的状态并避免过多的使用if-else语句,让我们的代码更加优雅。

    结构

    State_1

    • Context(上下文):定义用户感兴趣的功能。保持State的引用,并将用户的请求转发给State去处理;
    • State(状态接口):用来定义与Context的一个特定状态相关的行为;
    • ConcreteState(具体状态):实现与Context的一个特定状态相关的行为;

    示例

           以场景中提到的火车为例,我们首先需要为火车的每个状态创建一个实现IState接口的类,以及一个上下文类Train来表示火车。如下。

    State_2

    public interface IState
    {
        Train Context { get; }
        void OpenDoor();
        void CloseDoor();
        void Run();
        void Stop();
    }
    
    public class OpenDoorState : IState
    {
        public Train Context { private set; get; }
    
        public OpenDoorState(Train context)
        {
            this.Context = context;
        }
    
        public void OpenDoor()
        {
            //当前状态已经是“开门”状态,不做任何操作
        }
    
        public void CloseDoor()
        {
            Console.WriteLine("[OpenDoorState]:关门");
            this.Context.State = TrainStateFactory.Instance.CreateCloseDoorState(this.Context);
        }
               
        public void Run()
        {
            throw new Exception("[OpenDoorState]:开门状态下禁止运行");
        }
    
        public void Stop()
        {
            //该操作只能在“运行”状态时使用,不做任何操作
        }
    }
    
    public class CloseDoorState : IState
    {
        public Train Context { private set; get; }
    
        public CloseDoorState(Train context)
        {
            this.Context = context;
        }
    
        public void OpenDoor()
        {
            Console.WriteLine("[CloseDoorState]:开门");
            this.Context.State = TrainStateFactory.Instance.CreateOpenDoorState(this.Context);
        }
    
        public void CloseDoor()
        {
            //当前状态已经是“关门”状态,不做任何操作
        }
    
        public void Run()
        {
            Console.WriteLine("[CloseDoorState]:开车");
            this.Context.State = TrainStateFactory.Instance.CreateRunState(this.Context);
        }
    
        public void Stop()
        {
            //该操作只能在“运行”状态时使用,不做任何操作
        }
    }
    
    public class RunState : IState
    {
        public Train Context { private set; get; }
    
        public RunState(Train context)
        {
            this.Context = context;
        }
    
        public void OpenDoor()
        {
            throw new Exception("[RunState]:运行状态下禁止开门");
        }
    
        public void CloseDoor()
        {
            //该操作只能在“开门”状态时使用,不做任何操作
        }
    
        public void Run()
        {
            //当前状态已经是“运行”状态,不做任何操作
        }
    
        public void Stop()
        {
            Console.WriteLine("[RunState]:停车");
            this.Context.State = TrainStateFactory.Instance.CreateStopState(this.Context);
        }
    }
    
    public class StopState : IState
    {
        public Train Context { private set; get; }
    
        public StopState(Train context)
        {
            this.Context = context;
        }
    
        public void OpenDoor()
        {
            Console.WriteLine("[StopState]:开门");
            this.Context.State = TrainStateFactory.Instance.CreateOpenDoorState(this.Context);
        }
    
        public void CloseDoor()
        {
            //该操作只能在“开门”状态时使用,不做任何操作
        }
    
        public void Run()
        {
            //该操作只能在“关门”状态时使用,不做任何操作
        }
    
        public void Stop()
        {
            //当前状态已经是“停止”状态,不做任何操作
        }
    }
    
    public class Train
    {
        private IState _stateObj = null;
        public IState State { set => this._stateObj = value; }
    
        public void OpenDoor()
        {
            this._stateObj.OpenDoor();
        }
    
        public void CloseDoor()
        {
            this._stateObj.CloseDoor();
        }
    
        public void Run()
        {
            this._stateObj.Run();
        }
    
        public void Stop()
        {
            this._stateObj.Stop();
        }
    }
    
    /// <summary>
    /// 火车状态的享元工厂
    /// </summary>
    public class TrainStateFactory
    {
        //单例模式
        private TrainStateFactory() { }
        private static TrainStateFactory _instance = null;
        private static object _sysLock = new object();
        public static TrainStateFactory Instance
            {
                get
                {
                    if(_instance == null)
                    {
                        lock (_sysLock)
                        {
                            if(_instance == null)
                            {
                                _instance = new TrainStateFactory();
                            }
                        }
                    }
    
                    return _instance;
                }
            }
    
        /// <summary>
        /// 状态的享元池
        /// </summary>
        private List<IState> StatePool { set; get; } = new List<IState>();
    
        public IState CreateOpenDoorState(Train context)
            {
                var state = this.StatePool.Find(t => object.ReferenceEquals(t.Context, context) && t is OpenDoorState);
    
                if(state == null)
                {
                    state = new OpenDoorState(context);
                    this.StatePool.Add(state);
                }
    
                return state;
            }
    
        public IState CreateCloseDoorState(Train context)
            {
                var state = this.StatePool.Find(t => object.ReferenceEquals(t.Context, context) && t is CloseDoorState);
    
                if (state == null)
                {
                    state = new CloseDoorState(context);
                    this.StatePool.Add(state);
                }
    
                return state;
            }
    
        public IState CreateRunState(Train context)
            {
                var state = this.StatePool.Find(t => object.ReferenceEquals(t.Context, context) && t is RunState);
    
                if (state == null)
                {
                    state = new RunState(context);
                    this.StatePool.Add(state);
                }
    
                return state;
            }
    
        public IState CreateStopState(Train context)
            {
                var state = this.StatePool.Find(t => object.ReferenceEquals(t.Context, context) && t is StopState);
    
                if (state == null)
                {
                    state = new StopState(context);
                    this.StatePool.Add(state);
                }
    
                return state;
            }
    }

           示例中,定义了四个实现了IState接口的类OpenDoorState、CloseDoorState、RunState和StopState来表示火车Train的4个基本状态。同时在这些状态类中保留了Train的引用以便在执行操作后修改Train的状态(这样做的好处是可以简化Train,使其只需要关心当前要执行的操作而不必关系当前的状态,但同时也意味着IState的实现类需要保留与其本身无关的引用)。由状态类来修改Train的状态类似观察者模式(状态类发布,Train类订阅),在此基础上我们增加了一个创建火车状态的享元工厂TrainStateFactory,因为在本例中IState的实现类只存在行为而并没有内部状态,所以可以将其作为享元共享。

    总结

           状态模式可以帮助我们减少条件语句if-else和switch-case的使用,将每一个条件分支(状态)封装到一个独立的类中,使得对象(封装了条件分支的类)可以不依赖其它的对象而独立的变化,并且可以很容易的增加新的状态,遵循了迪米特法则。但状态模式对开闭原则并不友好。无论如何,在增加新的状态时都需要修改那些负责转换状态的逻辑,也必然会增加系统中类和对象的个数。因为状态模式的结构与实现都较为复杂,所以增加了维护的难度。


           以上,就是我对状态模式的理解,希望对你有所帮助。

           示例源码:https://gitee.com/wxingChen/DesignPatternsPractice

           系列汇总:https://www.cnblogs.com/wxingchen/p/10031592.html

           本文著作权归本人所有,如需转载请标明本文链接(https://www.cnblogs.com/wxingchen/p/10208275.html)

    • 相关阅读:
      Redis面试题
      redis基本操作
      pwd命令和cd命令
      ls命令详解
      Python时间操作所相关
      Nginx
      网络相关知识
      LeetCode 刷题记录(6-10题)
      绕过校园网Web认证
      Java相关知识
    • 原文地址:https://www.cnblogs.com/wxingchen/p/10208275.html
    Copyright © 2011-2022 走看看