• 【状态模式】 State Pattern


     我们先设计一个场景,饮料自动售卖机,来设计一下它的出售流程。

     流程图中,我们可把这个过程看成几个状态: 投币状态,选择饮料状态,售出状态,出售完毕状态.

    ,有了这个四个状态,我们设计一下界面(很粗略):

     

    在这里我们只定义了三种饮料和一个投币口,在设计接口和类之前,我们来看一下状态模式的UML图:

     

     State接口定义了一个所有具体状态的共同接口;任何状态都实现这个相同的接口,这样一来,状态之间可以互相代替.

     ConcreteState(具体状态)处理来自Context请求。每一个ConcreteState都提供了它自己对于请求的实现。所以,当Context改变状态时行为也跟着改变。

     Context(上下文)是一个类,它可以拥有一些内部状态。

     根据成熟的状态模式UML图,我们来设计我们自己的UML类图:

     

    直接上代码(可能和UML图有些出入,多了些参数,图主要是搭出一个架构来)

    实体类: 

      public class SoftDrink
        {
            public string Name { get; set; }
            public int Price { get; set; }
    
            public int Count { get; set; }
    
            private SoftdrinkMachineContext _context = new SoftdrinkMachineContext();
    
            public SoftdrinkMachineContext Context
            {
                get { return _context; }
            }
        }
    View Code

    创建获取某种饮料的工厂:

     public class SoftDrinkFactory
        {
            public static SoftDrink GetSoftDrink(string softDrinkName)
            {
                SoftDrink softDrink = SoftDrinkRepository.SoftDrinks.SingleOrDefault(s => s.Name.Equals(softDrinkName));
    
                if (softDrink == null)
                    throw new ArgumentException(string.Format("没有该饮料:{0}", softDrinkName));
    
                return softDrink;
            }
        }
    View Code

    饮料的存储库

     public class SoftDrinkRepository
        {
    
          
            static SoftDrinkRepository()
            {
                SoftDrinks = new List<SoftDrink>();
                AddSoftDrink(new SoftDrink { Name = "可乐", Count = 5, Price = 3 });
                AddSoftDrink(new SoftDrink { Name = "果粒橙", Count = 1, Price = 3 });
                AddSoftDrink(new SoftDrink { Name = "咖啡", Count = 5, Price = 3 });
            }
    
            public static List<SoftDrink> SoftDrinks { get; private set; }
    
    
            public static void AddSoftDrink(SoftDrink softDrink)
            {
                if (SoftDrinks.Any(s => s.Name.Equals(softDrink.Name)))
                {
                    SoftDrink needtobeAddedSoftdrink = SoftDrinks.Single(s => s.Name.Equals(softDrink.Name));
    
                    needtobeAddedSoftdrink.Price = softDrink.Price;
                    needtobeAddedSoftdrink.Count = softDrink.Count;
                }
    
                SoftDrinks.Add(softDrink);
            }
        }
    View Code

    这里存钱的地方,相当于一个银行,用的是静态

      public class Constants
        {
            public static int StoredMoney { get; set; }
        }
    View Code

    接口:

     public interface ISoftdrinkState
        {
            void InsertQuarter();
            void SelectSoftdrink(SoftDrink softDrink);
            void Dispense(SoftDrink softDrink);
            void SoldOut();
        }
    View Code

    这里的四个实现类代表四种状态,整个状态切换由内部自动完成.

    public abstract class AbstractSoftdrinkMacheState : ISoftdrinkState
        {
            protected SoftdrinkMachineContext _softdrinkMachineContext;
    
            protected AbstractSoftdrinkMacheState(SoftdrinkMachineContext softdrinkMachineContext)
            {
    
                _softdrinkMachineContext = softdrinkMachineContext;
            }
    
    
            public abstract void InsertQuarter();
            public abstract void SelectSoftdrink(SoftDrink softDrink);
            public abstract void Dispense(SoftDrink softDrink);
            public abstract void SoldOut();
    
    
        }
    
        public class NoQuarterState : AbstractSoftdrinkMacheState
        {
            public NoQuarterState(SoftdrinkMachineContext softdrinkMachineContext)
                : base(softdrinkMachineContext)
            {
            }
    
            public override void InsertQuarter()
            {
                _softdrinkMachineContext.SetState(_softdrinkMachineContext.HasQuarterState);
    
            }
    
            public override void SelectSoftdrink(SoftDrink softDrink)
            {
                throw new InvalidOperationException("你需要先投币");
            }
    
            public override void Dispense(SoftDrink softDrink)
            {
                throw new InvalidOperationException("你需要先投币");
            }
    
            public override void SoldOut()
            {
                throw new InvalidOperationException("你需要先投币");
            }
        }
    
        public class HasQuarterState : AbstractSoftdrinkMacheState
        {
            public HasQuarterState(SoftdrinkMachineContext softdrinkMachineContext)
                : base(softdrinkMachineContext)
            {
            }
    
            public override void InsertQuarter()
            {
    
            }
    
            public override void SelectSoftdrink(SoftDrink softDrink)
            {
                if (softDrink.Price > Constants.StoredMoney)
                    throw new InvalidOperationException("钱不够,请重新选择或补足");
                _softdrinkMachineContext.SetState(_softdrinkMachineContext.SoldState);
    
            }
    
            public override void Dispense(SoftDrink softDrink)
            {
                throw new InvalidOperationException("你需要先选择饮料");
            }
    
            public override void SoldOut()
            {
                throw new InvalidOperationException("你需要先选择饮料");
            }
        }
    
        public class SoldState : AbstractSoftdrinkMacheState
        {
            public SoldState(SoftdrinkMachineContext softdrinkMachineContext)
                : base(softdrinkMachineContext)
            {
            }
    
            public override void InsertQuarter()
            {
                throw new InvalidOperationException("请点击选择完毕按");
            }
    
            public override void SelectSoftdrink(SoftDrink softDrink)
            {
                throw new InvalidOperationException("请点击选择完毕按");
            }
    
            public override void Dispense(SoftDrink softDrink)
            {
                softDrink.Count--;
                Constants.StoredMoney -= softDrink.Price;
                _softdrinkMachineContext.SetState(softDrink.Count > 0
                    ? _softdrinkMachineContext.NoQuarterState
                    : _softdrinkMachineContext.SoldOutState);
            }
    
            public override void SoldOut()
            {
                throw new InvalidOperationException("请点击选择完毕按");
            }
        }
    
        public class SoldOutState : AbstractSoftdrinkMacheState
        {
            public SoldOutState(SoftdrinkMachineContext softdrinkMachineContext)
                : base(softdrinkMachineContext)
            {
            }
    
            public override void InsertQuarter()
            {
              
            }
    
            public override void SelectSoftdrink(SoftDrink softDrink)
            {
                throw new InvalidOperationException("已经售完");
            }
    
            public override void Dispense(SoftDrink softDrink)
            {
                throw new InvalidOperationException("已经售完");
            }
    
            public override void SoldOut()
            {
                throw new InvalidOperationException("已经售完");
            }
        }
    View Code

    上下文Context

    public class SoftdrinkMachineContext
        {
            private ISoftdrinkState _state;
    
            public SoftdrinkMachineContext()
            {
                NoQuarterState = new NoQuarterState(this);
                SoldOutState = new SoldOutState(this);
                HasQuarterState = new HasQuarterState(this);
                SoldState = new SoldState(this);
    
                _state = NoQuarterState;
            }
    
            public ISoftdrinkState SoldOutState { get; private set; }
            public ISoftdrinkState NoQuarterState { get; private set; }
            public ISoftdrinkState HasQuarterState { get; private set; }
            public ISoftdrinkState SoldState { get; private set; }
    
            public void InsertQuarter()
            {
                _state.InsertQuarter();
    
            }
    
            public void SelectSoftdrink(SoftDrink softDrink)
            {
                _state.SelectSoftdrink(softDrink);
            }
    
            public void Dispense(SoftDrink softDrink)
            {
                _state.Dispense(softDrink);
            }
    
            public void SoldOut()
            {
                _state.SoldOut();
            }
    
            public void SetState(ISoftdrinkState state)
            {
                _state = state;
            }
        }
    View Code

    Form窗口类

    public partial class Form1 : Form
        {
            private SoftDrink _currSoftDrink;
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                ActionWithAlertingErrorInfo(() =>
                {
                    _currSoftDrink.Context.Dispense(_currSoftDrink); 
                    MessageBox.Show(string.Format("退出{0}钱", Constants.StoredMoney));
                    Constants.StoredMoney = 0;
                });
    
            }
    
            private void btnInputMoney_Click(object sender, EventArgs e)
            {
                var money = 0;
                if (int.TryParse(txtMoneyInput.Text, out money))
                {
                    Constants.StoredMoney += money;
                    ActionWithAlertingErrorInfo(() => SoftDrinkRepository.SoftDrinks.ForEach(c => c.Context.InsertQuarter()));
                }
            }
    
            private void btnCoke_Click(object sender, EventArgs e)
            {
                var btn = sender as Button;
                var index = btn.Text.IndexOf("(", StringComparison.Ordinal);
                var softDrinkName = btn.Text.Substring(0, index);
    
                _currSoftDrink = SoftDrinkFactory.GetSoftDrink(softDrinkName);
    
                ActionWithAlertingErrorInfo(() => _currSoftDrink.Context.SelectSoftdrink(_currSoftDrink));
            }
    
            public void ActionWithAlertingErrorInfo(Action action)
            {
                try
                {
                    action.Invoke();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
    View Code

     这里加入了一个Softdrink类,因为由于每一种饮料都有自己的状态,所以对每个饮料都要有一个上下文的类。

    后记: 

    最近一个朋友告诉我,她怎么都觉得策略模式和状态模式是一样,既然名字不一样,功能肯定不一样,该去怎么区分。

    策略模式和状态模式可以说是同胞兄弟,但是本质还是有些区别.

      策略模式->定义算法族,分别封装起来,让它们之间可以互相替换(手动),此模式让算法的变化独立于使用算法的客户。

      状态模式->允许对象在内部状态改变时改变它的行为(自动),对象看来好像修改了它的类。

    也就是说,状态模式利用许多不同的状态对象,当Context对象随着时间而改变装,而任何的状态改变都是定义好的。换句话说,“改变行为”这件事是建立在我状态模式自己方案中的,而策略模式并没有一组状态标记,而更多由调用者去使用。

  • 相关阅读:
    mpvue 引入 vant-weapp 踩坑记录
    mac上hbuilder无法启动微信小程序调试窗口的解决办法
    mac 安装了xcode,flutter doctor 却检测不到展示叉叉
    vue 前端复制粘贴方式上传图片
    401 错误时,几个细节检查
    vue 图片src动态加载
    前端优化的大方向
    how to stop code runner in vscode(macOs)
    window server 2008 r2 TLS 升级1.2
    超时时间已到。超时时间已到,但是尚未从池中获取连接。出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小
  • 原文地址:https://www.cnblogs.com/guochenkai/p/3977787.html
Copyright © 2020-2023  润新知