正文的概念性内容来自深入显出设计格局一书

品种须求

澳门凯旋门游戏网址 1

这是三个糖果机的须要图. 

它有多样景况, 分别是图中的四个圆形:

  • No Quarter: 无硬币
  • Has Quater 有硬币
  • Gumball Sold 糖果卖出
  • Out of Gumball 未有糖果了

澳门凯旋门游戏网址凯旋门074网址,本条图很像二个状态图. 种种圆圈正是多少个情形,
每一个带箭头的线正是景况的转变.

以此供给用文字描述便是: 糖果机在没投硬币的时候, 能够投硬币, 投完硬币,
搬入手柄, 糖果就能出去, 如葡萄糖果机里未有糖果了, 那么就比十分小概再卖糖果了.

伊始设计

本条须要看起来依然蛮轻便的, 作者想可以这样来完毕:

  1. 重新整建好全体的状态, 一共有4个:

澳门凯旋门游戏网址 2

  1. 开创三个实例变量来保存当前的地方, 并为各样情形定义多个值:

澳门凯旋门游戏网址 3

  1. 收拾好系统中只怕产生的动作:

澳门凯旋门游戏网址 4

  1. 创办四个类作为状态机, 针对每一个动作, 我们创造三个方法,
    在章程里我们应用准绳语句来调整在各样意况中该表现是还是不是合理. 比方,
    投入硬币后, 大家或者需求上面那个方法:

澳门凯旋门游戏网址 5

留心: 最终叁个if中, 有改观状态的动作(假如在此之前的景况是一贯不硬币,
那么投入硬币后, 状态应改为有硬币).

下边来得以实现那些状态机:

澳门凯旋门游戏网址 6

澳门凯旋门游戏网址 7

澳门凯旋门游戏网址 8

澳门凯旋门游戏网址 9

澳门凯旋门游戏网址 10

代码量照旧比相当的大啊, 那之中根本做的正是在各种动作方法里, 判别各样意况,
如何客观就改动状态.

运营一下:

澳门凯旋门游戏网址 11

结果:

澳门凯旋门游戏网址 12

看起来整个都OK了, 直到:

供给变动

糖果机COO说, 作者想买糖果产生二个戏耍,
投硬币买糖果的人中的百分之十在移动手柄后将会博得五个糖果并不是三个.

近些日子的动静开头有一些乱了:

澳门凯旋门游戏网址 13

乘胜必要的浮动, 我们规划会促成更为多的bug…

回首一下统筹标准: “把转换的一些包装起来” 和 “尽量使用组合”.
大家得以把各个意况的展现放到它和睦的类里面,
然后各类动作只供给贯彻本人意况下的动作就能够.
并且或然糖果机能够运用情状对象来寄托代表自身日前的状态.

双重规划

此番我们就把状态的作为封装到种种状态对象里面,
并在动作发生的时候委托到日前的状态.

1. 先是, 定义二个意况接口, 那么些接口满含糖果机的每一个动作

2. 针对种种状态, 完结八个有血有肉的景色类.
这几个类将担当糖果机在改状态下的行为.

3. 末尾, 去掉那三个条件推断代码, 把这么些专门的学业委托给状态类.

地点要落实的正是气象情势 (State Pattern).

把叁个景观有所的一言一动放到八个类里面, 那样,
就贯彻了本地化何况有助于修改和通晓
.

规划类图:

澳门凯旋门游戏网址 14

此间大家接纳景况类来代替初版设计中的数值.

本来别忘了那几个情况:

澳门凯旋门游戏网址 15

近年来自己直接使用C#达成那个情状:

本文的概念性内容出自深入显出设计方式一书凯旋门074网址。情景接口:

namespace StatePattern.Abstractions
{
    public interface IState
    {
        void InjectQuarter();
        void EjectQuarter();
        void TurnCrank();
        void Dispense();
    }
}

两个状态, 有硬币:

using System;
using StatePattern.Abstractions;
using StatePattern.Machines;

namespace StatePattern.States
{
    public class HasQuarterState : IState
    {
        private readonly GumballMachine _gumballMachine;
        private readonly Random _random = new Random();

        public HasQuarterState(GumballMachine gumballMachine)
        {
            _gumballMachine = gumballMachine;
        }

        public void InjectQuarter()
        {
            Console.WriteLine("You can’t insert another quarter");
        }

        public void EjectQuarter()
        {
            Console.WriteLine("Quarter returned");
            _gumballMachine.State = _gumballMachine.NoQuarterState;
        }

        public void TurnCrank()
        {
            Console.WriteLine("You turned...");
            var winner = _random.Next(0, 10);
            if (winner == 0 && _gumballMachine.Count > 1)
            {
                _gumballMachine.State = _gumballMachine.WinnerState;
            }
            else
            {
                _gumballMachine.State = _gumballMachine.SoldState;
            }
        }

        public void Dispense()
        {
            Console.WriteLine("No gumball dispensed");
        }

        public override string ToString()
        {
            return "just being inserted with a quarter";
        }
    }
}

无硬币:

澳门凯旋门游戏网址 16澳门凯旋门游戏网址 17

using System;
using StatePattern.Abstractions;
using StatePattern.Machines;

namespace StatePattern.States
{
    public class NoQuarterState: IState
    {
        private readonly GumballMachine _gumballMachine;

        public NoQuarterState(GumballMachine gumballMachine)
        {
            _gumballMachine = gumballMachine;
        }

        public void InjectQuarter()
        {
            Console.WriteLine("You inserted a quarter");
            _gumballMachine.State = _gumballMachine.HasQuarterState;
        }

        public void EjectQuarter()
        {
            Console.WriteLine("You havn't inserted a quarter");
        }

        public void TurnCrank()
        {
            Console.WriteLine("You turned, but there is no quarter");
        }

        public void Dispense()
        {
            Console.WriteLine("You need to pay first");
        }

        public override string ToString()
        {
            return "is Waiting for quarter";
        }
    }
}

View Code

卖光了:

澳门凯旋门游戏网址 18澳门凯旋门游戏网址 19

using System;
using StatePattern.Abstractions;
using StatePattern.Machines;

namespace StatePattern.States
{
    public class SoldOutState: IState
    {
        private readonly GumballMachine _gumballMachine;

        public SoldOutState(GumballMachine gumballMachine)
        {
            _gumballMachine = gumballMachine;
        }

        public void InjectQuarter()
        {
            Console.WriteLine("You can’t insert a quarter, the machine is sold out");
        }

        public void EjectQuarter()
        {
            Console.WriteLine("You can’t eject, you haven’t inserted a quarter yet");
        }

        public void TurnCrank()
        {
            Console.WriteLine("You turned, but there are no gumballs");
        }

        public void Dispense()
        {
            Console.WriteLine("No gumball dispensed");
        }

        public override string ToString()
        {
            return "is sold out";
        }
    }
}

View Code

无唯有偶卖出糖果:

澳门凯旋门游戏网址 20澳门凯旋门游戏网址 21

using System;
using StatePattern.Abstractions;
using StatePattern.Machines;

namespace StatePattern.States
{
    public class SoldState : IState
    {
        private readonly GumballMachine _gumballMachine;

        public SoldState(GumballMachine gumballMachine)
        {
            _gumballMachine = gumballMachine;
        }

        public void InjectQuarter()
        {
            Console.WriteLine("Please wait, we’re already giving you a gumball");
        }

        public void EjectQuarter()
        {
            Console.WriteLine("Sorry, you already turned the crank");
        }

        public void TurnCrank()
        {
            Console.WriteLine("Turning twice doesn’t get you another gumball!");
        }

        public void Dispense()
        {
            _gumballMachine.ReleaseBall();
            if (_gumballMachine.Count > 0)
            {
                _gumballMachine.State = _gumballMachine.NoQuarterState;
            }
            else
            {
                Console.WriteLine("Oops, out of gumballs!");
                _gumballMachine.State = _gumballMachine.SoldOutState;
            }
        }

        public override string ToString()
        {
            return "just sold a gumball";
        }
    }
}

本文的概念性内容出自深入显出设计方式一书凯旋门074网址。View Code

中奖了:

澳门凯旋门游戏网址 22澳门凯旋门游戏网址 23

using System;
using StatePattern.Abstractions;
using StatePattern.Machines;

namespace StatePattern.States
{
    public class WinnerState: IState
    {
        private readonly GumballMachine _gumballMachine;

        public WinnerState(GumballMachine gumballMachine)
        {
            _gumballMachine = gumballMachine;
        }

        public void InjectQuarter()
        {
            Console.WriteLine("Please wait, we’re already giving you a gumball");
        }

        public void EjectQuarter()
        {
            Console.WriteLine("Sorry, you already turned the crank");
        }

        public void TurnCrank()
        {
            Console.WriteLine("Turning twice doesn’t get you another gumball!");
        }

        public void Dispense()
        {
            Console.WriteLine("YOU'RE A WINNER! You get two balls for you 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("Oops, out of gumballs!");
                    _gumballMachine.State = _gumballMachine.SoldOutState;
                }
            }
        }

        public override string ToString()
        {
            return "just sold 2 gumballs";
        }
    }
}

View Code

糖果机:

using System;
using StatePattern.Abstractions;
using StatePattern.States;

namespace StatePattern.Machines
{
    public class GumballMachine
    {
        public IState SoldOutState { get; set; }
        public IState NoQuarterState { 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 numberOfGumballs)
        {
            SoldState = new SoldState(this);
            NoQuarterState = new NoQuarterState(this);
            HasQuarterState = new HasQuarterState(this);
            SoldOutState = new SoldOutState(this);
            WinnerState = new WinnerState(this);

            Count = numberOfGumballs;
            if (Count > 0)
            {
                State = NoQuarterState;
            }
        }

        public void InjectQuarter()
        {
            State.InjectQuarter();
        }

        public void EjectQuarter()
        {
            State.EjectQuarter();
        }

        public void TurnCrank()
        {
            State.TurnCrank();
            State.Dispense();
        }

        public void ReleaseBall()
        {
            Console.WriteLine("A gumball comes rolling out the slot...");
            if (Count != 0)
            {
                Count--;
            }
        }

        public void Refill(int count)
        {
            Count += count;
            State = NoQuarterState;
        }

        public override string ToString()
        {
            return $"Mighty Gumball, Inc.\nC#-enabled Standing Gumball Model #2018\nInventory: {Count} gumballs\nThe machine {State} ";
        }
    }
}

专注糖果机里面的情状使用的是指标实际不是原先的数值本文的概念性内容出自深入显出设计方式一书凯旋门074网址。. 

运行:

using System;
using StatePattern.Machines;

namespace StatePattern
{
    class Program
    {
        static void Main(string[] args)
        {
            var originalColor = Console.ForegroundColor;

            var machine = new GumballMachine(5);
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.WriteLine(machine);

            Console.ForegroundColor = originalColor;
            Console.WriteLine();
            machine.InjectQuarter();
            machine.TurnCrank();
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.WriteLine(machine);

            Console.ForegroundColor = originalColor;
            Console.WriteLine();
            machine.InjectQuarter();
            machine.TurnCrank();
            machine.InjectQuarter();
            machine.TurnCrank();
            machine.InjectQuarter();
            machine.TurnCrank();
            machine.InjectQuarter();
            machine.TurnCrank();
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.WriteLine(machine);

            Console.ReadKey();
        }
    }
}

澳门凯旋门游戏网址 24

我们做了怎么?

咱俩修改了计划的结构, 不过法力是大同小异的:

  • 把各类意况的一举一动本地化到它协和的类里面了
  • 移除了独具情形决断代码, 他们也很难维护.
  • 对各类情状的修改关闭, 然则让糖果机依旧能够扩张 (加多WINNE福睿斯 状态)
  • 创办了二个与供给图大概应有尽有对应的代码库和类协会, 便于掌握.

澳门凯旋门游戏网址 25

澳门凯旋门游戏网址 26

澳门凯旋门游戏网址 27

本文的概念性内容出自深入显出设计方式一书凯旋门074网址。动静情势定义

景况形式允许一个对象在其间境况更动的时候能够修改它自个儿的行为.
对象就像是修改了它的类
.

第二句可以如此敞亮: 

从客户的见识, 假若一个你采用的靶子能够完全改观它的展现,
那么那些指标看起来如同从其他类初始化出来的一律 (变了叁个类).
而事实上呢, 你使用的是组成的情势来促成变类的功力,
具体到大家的档期的顺序正是引用分化的事态对象.

类图:

澳门凯旋门游戏网址 28

Context(上下文情状)正是有着多数中间景况的类, 糖果机.

每当request()发生在Context上的时候, 它就被托付给了当时的图景对象.
左侧正是种种意况对象.

相比一下政策方式和情景格局

那多个情势表面上看起来也有一点点像, 不过实在它们的目的不同的.

事态方式下, 大家把一套行为封装在状态对象里; 任何要给每一日,
Context总是委托专门的学问给内部的二个对象. 随着岁月的变化,
Context的当前情景对象也发生变化, 所以Context的表现也随即变化.
客户对意况对象通晓的很少.

计划形式下, 客户要钦点攻略对象和Context组合.
该形式允许在运营时灵活的退换政策, 平常会有一个最契合登时条件的战术.

完整来讲,

计策方式是对接轨的灵活替换. 使用持续来定义类的行为, 当你供给退换的时候,
那些作为还大概会在的, 使用政策形式然则组成分裂的靶子来退换行为.

意况形式则是一大堆条件剖断的替代者, 把作为封装在情景对象里,
就能够大致的通过切换状态对象来更换Context的行为.

其余难点

Q: 总是由现实的景况对象来调整状态的走向吧?

A: 亦非, 可以用Context决定状态的走向.

Q: 客户直接待上访谈状态吧?

A: 客户不直接更改状态.

Q: 借使Context有众多实例, 那么能够分享状态对象啊?

A: 能够, 这么些也再三产生. 但这供给您的情况对象不得以保存它们的在那之中情形,
不然各类Context都亟需三个单身的实例.

Q: 这么设计看起来类居多啊!

A: 是呀, 但是足以让对客户可知的类的个数比较少, 这一个数目才第一

Q: 能够选拔抽象类来替代State接口吗?

A: 能够, 假诺须求有的公家艺术的话.

总结

明天的相比较轻易, 唯有这一个定义:

事态方式允许几个指标在内部景色改造的时候能够修改它谐和的行为.
对象就像是修改了它的类
.

 

该连串的源码在: 

相关文章