首页 > 技术文章 > 【状态模式】 State Pattern

guochenkai 2014-09-17 18:17 原文

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

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

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

 

在这里我们只定义了三种饮料和一个投币口,在设计接口和类之前,我们来看一下状态模式的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对象随着时间而改变装,而任何的状态改变都是定义好的。换句话说,“改变行为”这件事是建立在我状态模式自己方案中的,而策略模式并没有一组状态标记,而更多由调用者去使用。

推荐阅读