二十三种设计模式[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)

    原文地址:https://www.cnblogs.com/wxingchen/p/10208275.html