【状态模式】 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; } } }
创建获取某种饮料的工厂:
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; } }
饮料的存储库
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); } }
这里存钱的地方,相当于一个银行,用的是静态
public class Constants
{
public static int StoredMoney { get; set; }
}
接口:
public interface ISoftdrinkState
{
void InsertQuarter();
void SelectSoftdrink(SoftDrink softDrink);
void Dispense(SoftDrink softDrink);
void SoldOut();
}
这里的四个实现类代表四种状态,整个状态切换由内部自动完成.
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("已经售完");
}
}
上下文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; } }
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); } }
这里加入了一个Softdrink类,因为由于每一种饮料都有自己的状态,所以对每个饮料都要有一个上下文的类。
后记:
最近一个朋友告诉我,她怎么都觉得策略模式和状态模式是一样,既然名字不一样,功能肯定不一样,该去怎么区分。
策略模式和状态模式可以说是同胞兄弟,但是本质还是有些区别.
策略模式->定义算法族,分别封装起来,让它们之间可以互相替换(手动),此模式让算法的变化独立于使用算法的客户。
状态模式->允许对象在内部状态改变时改变它的行为(自动),对象看来好像修改了它的类。
也就是说,状态模式利用许多不同的状态对象,当Context对象随着时间而改变装,而任何的状态改变都是定义好的。换句话说,“改变行为”这件事是建立在我状态模式自己方案中的,而策略模式并没有一组状态标记,而更多由调用者去使用。