• C#设计模式之10:状态模式


    状态模式

    状态模式将合适的Context(上下文)模拟成一个状态机,在这个状态机的内部,由Context来决定整个状态机的状态,再根据不同的状态执行不同的方法。在C#编译器的帮助下,很多语法糖的背后都是一个状态机的实现,比如在一个返回IEnumerale或者IEnumeartor的方法中使用yield return,就会在后面建立一个状态机,再比如await一个可等待(或者说实现了GetAwaiter方法的类型,比如Task)的类型的时候,编译器也会在后台建立一个状态机类来根据state字段的不同的值来执行不同的逻辑。

    状态模式的UML于策略模式相似(strategy patern):

    不同之处在于他们的意图不同:策略模式需要客户(调用者)主动的去指定Context需要组合的策略对象是哪一个。而状态模式的策略对象(就是上图中的state)一般是固定在业务执行流程中的。

    一般来说,我们把策略模式想象成除了继承之外的一种弹性替代方案,如果使用继承定义了一个类的行为,这种行为是静态编译时就确定的,你将被这个行为困住,甚至修改它都会很困难,有了策略模式,你可以通过组合不同的对象来改变行为,

    我们把状态模式想成是不用在Context中放置很多if else条件判断的替代方案。通过将行为封装到不同的状态对象中,你可以通过在Context内简单的改变状态对象来改变Context的行为。

    状态模式的定义:允许对象在内部状态改变时改变它的行为,对象看起来好像改变了它的类。

    在C#的委托的帮助下,我们实现状态模式实际上可以用事件机制来进行实现。比如说某个类中有很多类似OnShitHappend方法这种的实现。这是我对这个模式的理解,但是还没有进行相应的实践。

    先来看一下使用这个模式的一个背景,书上的讲的是糖果公司为了扩展业务,需要对糖果机进行扩展,并加入一些营销的手段,这个糖果机的使用流程是①投入硬币②转动摇杆③糖果从槽子里面出来:

    从上图看以看出这个糖果机有几个状态:

    如果不适用状态模式来实现这个糖果机的代码,那里面肯定是一堆if else的语句,这不是最糟糕的,最糟糕的是如果要加入额外的状态那就不得不重新考虑所有的逻辑,补充新的if else。。。是吧?

    那直接来看使用状态模式实现的吧:

     public interface IState
        {
            void InsertQuarter();
            void EjectQuarter();
            void TurnCrank();
            void Dispense();
        }

    首先定义一个IState的接口,这个接口定义了糖果机内部的所有动作。因为糖果机在这里被模拟成一个状态机,这个状态机的状态的改变都是根据不同动作的执行来连带的。

     public class GumballMachine
        {
            public IState NOquarterState { get; set; }
            public IState SoldOutState { get; set; }
            public IState HasQuarterState { get; set; }
            public  IState SoldState { get; set; }
            public IState WinnerState { get; set; }
            public IState State { get; set; }
            public int Count { get; set; }
    
            public GumballMachine(int numberGumballs)
            {
                SoldOutState = new SoldOutState(this);
                NOquarterState = new NoQuarterState(this);
                HasQuarterState = new HasQuarterState(this);
                SoldState = new SoldState(this);
                WinnerState=new WinnerState(this);
                State = NOquarterState;
                Count = numberGumballs;
            }
    
            public void InsertQuarter()
            {
                State.InsertQuarter();
            }
    
            public void EjectQuarter()
            {
                State.EjectQuarter();
            }
            public void TrunCrank()
            {
                State.TurnCrank();
                State.Dispense();
            }
            public void ReleaseBall()
            {
                Console.WriteLine("a gumball come rolling out the slot");
                if (Count!=0)
                {
                    Count--;
                }
            }
        }

    然后定义这个糖果机。在糖果机内部有很多不同的状态,状态会根据执行不同的方法而改变。

    然后定义这些状态:首先定义没有投入硬币的状态对象。糖果机的初始状态就是这个状态。

    public class NoQuarterState : IState
        {
            private readonly GumballMachine _gumballMachine;
    
            public NoQuarterState(GumballMachine gumballMachine)
            {
                _gumballMachine = gumballMachine;
            }
            public void InsertQuarter()
            {
                Console.WriteLine("now you insert a quarter!");
                _gumballMachine.State = _gumballMachine.HasQuarterState;
            }
    
            public void EjectQuarter()
            {
                Console.WriteLine("you have not inserted a quarter!");
            }
    
            public void TurnCrank()
            {
                Console.WriteLine("you turned ,but there is not a quarter!");
            }
    
            public void Dispense()
            {
                Console.WriteLine("you need to pay first!");
            }
        }

    投入硬币后就会进入有硬币的状态:

    public class HasQuarterState : IState
        {       
            private readonly GumballMachine _gumballMachine;
            public HasQuarterState(GumballMachine gumballMachine)
            {
                _gumballMachine = gumballMachine;
            }
            public void InsertQuarter()
            {
                Console.WriteLine("you can not insert another quarter!");
            }
    
            public void EjectQuarter()
            {
                Console.WriteLine("quarter's returned");
                _gumballMachine.State = _gumballMachine.NOquarterState;
            }
    
            public void TurnCrank()
            {
                Console.WriteLine("you turned...");
                int winner=new Random().Next(0,10);
                if (winner==0&&_gumballMachine.Count>2)
                {
                    _gumballMachine.State = _gumballMachine.WinnerState;
                }
                else
                {
                    _gumballMachine.State = _gumballMachine.SoldState;
                }            
            }
    
            public void Dispense()
            {
                Console.WriteLine("no gumball dispensed!");
            }
        }

    硬币投进去后紧接着就会转动摇杆,然后就会进入售卖状态:

    public class SoldState : IState
        {
            private readonly GumballMachine _gumballMachine;
    
            public SoldState(GumballMachine gumballMachine)
            {
                _gumballMachine = gumballMachine;
            }
            public void InsertQuarter()
            {
                Console.WriteLine("please wait,we're already turned the crank!");
            }
    
            public void EjectQuarter()
            {
                Console.WriteLine("sorry,we are already turned the crank!");
            }
    
            public void TurnCrank()
            {
                Console.WriteLine("turning twice dose not give you another gumball!");
            }
    
            public void Dispense()
            {
                _gumballMachine.ReleaseBall();
                if ((_gumballMachine.Count>0))
                {
                    _gumballMachine.State = _gumballMachine.NOquarterState;
                }
                else
                {
                    Console.WriteLine("opps,out of gumballs!");
                    _gumballMachine.State = _gumballMachine.SoldOutState;
                }
            }
        }

    在售卖状态,要做的就是糖果从槽子里面出来,当糖果出来后,我们要进行一些判断:如果还有糖果,接下来我们就进入初始状态,就是NoQuarterState状态,如果没有糖果,就会进入售完状态:

     public class SoldOutState : IState
        {
            private readonly GumballMachine _gumballMachine;
    
            public SoldOutState(GumballMachine gumballMachine)
            {
                _gumballMachine = gumballMachine;
            }
            public void InsertQuarter()
            {
                Console.WriteLine("sorry,the gumballMachine's sold out!");
                _gumballMachine.State = _gumballMachine.HasQuarterState;
            }
    
            public void EjectQuarter()
            {
                Console.WriteLine("now return your quarter!");
                _gumballMachine.State = _gumballMachine.NOquarterState;
            }
    
            public void TurnCrank()
            {
                Console.WriteLine("sorry,the gumballmachine's sold out!");
            }
    
            public void Dispense()
            {
                Console.WriteLine("sorry,,the gumbalmachine's sold out!");
            }
        }

    当我们需要开展一些营销活动时,可以扩展IState接口,来创建一个营销类:

    public class WinnerState:IState
        {
            private readonly GumballMachine _gumballMachine;
            public WinnerState(GumballMachine gumallMachine)
            {
                _gumballMachine = gumallMachine;
            }
            public void InsertQuarter()
            {
                Console.WriteLine("invalid operation..");
            }
    
            public void EjectQuarter()
            {
                Console.WriteLine("invalid operation..");
            }
    
            public void TurnCrank()
            {
                Console.WriteLine("invliad operation..");
            }
    
            public void Dispense()
            {
                Console.WriteLine("congratulations!you are the winner!you'll get two gumballs with one quarter!");
                _gumballMachine.ReleaseBall();
                if (_gumballMachine.Count==0)
                {
                    _gumballMachine.State = _gumballMachine.SoldOutState;
                }
                else
                {
                    _gumballMachine.ReleaseBall();
                    if (_gumballMachine.Count >0)
                    {
                        _gumballMachine.State = _gumballMachine.NOquarterState;
                    }
                    else
                    {
                        Console.WriteLine("out of gumballs!");
                        _gumballMachine.State = _gumballMachine.SoldOutState;
                    }
                }
            }
        }

    这个winnerstate类可以在转动摇杆的逻辑中放入。具体参见上面的HasQuarterState类。

  • 相关阅读:
    处处留心皆学问,世事如棋局局新…
    【转载】2017 软件测试行业现状调查报告_From_51testing_On_20180625
    Hello World In Go ...
    C# 易错题整理
    C# 函数
    C# 哈希表,队列,栈
    C# 数组,集合,泛型集合
    C# 如何生成验证码
    C# 年月日时间题+Timespan
    闰年的一个BUG
  • 原文地址:https://www.cnblogs.com/pangjianxin/p/8821239.html
Copyright © 2020-2023  润新知