Loading

设计模式篇——初探状态模式

状态模式简介:

状态模式允许一个对象基于内部状态而拥有不同的行为,这个对象看起来就好像修改了它的类。

Context将行为委托给当前状态对象。

把每个状态封装进一个类中,以此来解耦和扩展

状态装换可以有State类或者Context类来控制

状态模式通常会导致设计中的类的数目大量增加

状态类可以被多个Context实例共享。

 

  假设我么现在有一个糖果机,可以投入“1块钱硬币“ ,转动把手,弹出一颗糖果。也可以不转动把手,把投入的钱币退回=》

 

  我们可以从这个过程中分析得出,这个过程一共有4个状态=》没有投币,已经投币,发放(出售)糖果,糖果售罄;基于这四个状态,也有四个动作投入钱币,退回钱币,转动把手,发放糖果(这个动作是在糖果机内部完成)。

  基于这个我们创建一个GumballMachine(糖果机)类,糖果机实现了三个行为(投币,退币,摇动把手),构造函数中传入初始放入的糖果数量。糖果机中存在一个State的抽象类(下文中实现),代表所有状态的一个父类。setState方法更新状态。releaseBall方法弹出糖果,GetCount方法返回当前糖果数量。

 1     public class GumballMachine
 2     {
 3         //所有的状态(初始化只用到soldOut状态和noMoneyState状态)
 4         static State soldOutState;  //售罄
 5         static State noMoneyState;  //没有投币
 6         //static State hasMoneyState; //已投币
 7         //static State soldState;     //出售
 8 
 9         private int _count; //糖果数量
10         private State _state = soldOutState; 
11         public GumballMachine(int count)
12         {
13             _count = count;
14             soldOutState = new SoldOutState(this);
15             noMoneyState = new NoMoneyState(this);
16             //hasMoneyState = new HasMoneyState(this);
17             //soldState = new SoldState(this);
18             if (_count > 0)
19                 _state = noMoneyState;      //糖果数量大于0,初始状态修改为未投币
20         }
21         public void insertMoney()           //投币
22         {
23             _state.insertMoney();
24         }
25         public void ejectMoney()            //退币
26         {   
27             _state.ejectMoney();
28         }
29 
30         /// <summary>
31         /// 转动把手
32         /// </summary>
33         public void turnCrank() 
34         {
35             _state.turnCrank();         //转动
36             _state.dispense();          //发放糖果
37         }          
38         /// <summary>
39         /// 更新状态
40         /// </summary>
41         /// <param name="state"></param>
42         public void setState(State state)
43         {
44             _state = state;
45         }
46         /// <summary>
47         /// 弹出糖果
48         /// </summary>
49         public void releaseBall()
50         {
51             Console.WriteLine("**************弹出一颗糖果●**************");
52             if (_count != 0)
53                 _count = _count - 1;
54         }
55         /// <summary>
56         /// 返回当前糖果状态
57         /// </summary>
58         /// <returns></returns>
59         public int GetCount()
60         {
61             return _count;
62         }
63     }
GumballMachine(糖果机)

  我们基于所有状态抽象出一个父类State,这个父类有四个抽象方法,分别代表四个动作。(PS:这里抽象类或者接口由使用场景选择,抽象类可以让所有子类都初始化一些操作。)

 1     /// <summary>
 2     /// 状态的抽象类,所有状态都要继承自它
 3     /// </summary>
 4     public abstract class State
 5     {
 6         public abstract void insertMoney(); //放入硬币
 7         public abstract void ejectMoney();  //取出硬币
 8         public abstract void turnCrank();   //转动把手
 9         public abstract void dispense();    //发放糖果
10     }
State抽象类

  接下来我们实现这四个状态。

 1     /// <summary>
 2     /// 没有投钱
 3     /// </summary>
 4     public class NoMoneyState : State
 5     {
 6         private readonly GumballMachine _GumballMachine;
 7         public NoMoneyState(GumballMachine GumballMachine)
 8         {
 9             _GumballMachine = GumballMachine;
10         }
11         public override void dispense()
12         {
13             Console.WriteLine("需要先投币才能发放糖果");
14         }
15 
16         public override void ejectMoney()
17         {
18             Console.WriteLine("没有投币不能退币");
19         }
20 
21         public override void insertMoney()
22         {
23             Console.WriteLine("投入了1块钱,可以转动手柄弹出糖果。");
24             _GumballMachine.setState(new HasMoneyState(_GumballMachine)); //状态转换
25         }
26 
27         public override void turnCrank()
28         {
29             Console.WriteLine("没有投币不能转动");
30         }
31     }
没有投币
 1     /// <summary>
 2     /// 已经投钱
 3     /// </summary>
 4     public class HasMoneyState : State
 5     {
 6         private readonly GumballMachine _GumballMachine;
 7         public HasMoneyState(GumballMachine GumballMachine)
 8         {
 9             _GumballMachine = GumballMachine;
10         }
11         public override void insertMoney()
12         {
13             Console.WriteLine("你已经投币,不需要重复投币,请等待。。");
14         }
15         public override void ejectMoney()
16         {
17             Console.WriteLine("退币。。现在机器是未投币");
18             _GumballMachine.setState(new NoMoneyState(_GumballMachine));
19         }
20         public override void turnCrank()
21         {
22             Console.WriteLine("摇动把手");
23             _GumballMachine.setState(new SoldState(_GumballMachine));
24         }
25         public override void dispense()
26         {
27             Console.WriteLine("发放糖果中(不正确的操作。。。)");
28         }
29     }
已经投币
 1  /// <summary>
 2     /// 发放(出售)糖果
 3     /// </summary>
 4     public class SoldState : State
 5     {
 6         private readonly GumballMachine _GumballMachine;
 7         public SoldState(GumballMachine GumballMachine)
 8         {
 9             _GumballMachine = GumballMachine;
10         }
11         public override void dispense()
12         {
13             _GumballMachine.releaseBall();  //发放糖果  ,判断糖果数量,修改状态
14             if (_GumballMachine.GetCount() > 0)
15                 _GumballMachine.setState(new NoMoneyState(_GumballMachine));
16             else
17             {
18                 Console.WriteLine("糖果售罄");
19                 _GumballMachine.setState(new SoldOutState(_GumballMachine));
20             }
21         }
22 
23         public override void ejectMoney()
24         {
25             Console.WriteLine("已转动把手,不能再退币(不正确动作)");
26         }
27 
28         public override void insertMoney()
29         {
30             Console.WriteLine("正在发放糖果,不要重复投币(不正确动作)");
31         }
32 
33         public override void turnCrank()
34         {
35             Console.WriteLine("不要重复转动把手(不正确操作)");
36         }
37     }
发放(出售)糖果
 1     /// <summary>
 2     /// 售罄
 3     /// </summary>
 4     public class SoldOutState : State
 5     {
 6         private readonly GumballMachine _GumballMachine;
 7         public SoldOutState(GumballMachine GumballMachine)
 8         {
 9             _GumballMachine = GumballMachine;
10         }
11         public override void dispense()
12         {
13             Console.WriteLine("糖果已售罄,不能发放糖果(不正确的操作)");
14         }
15 
16         public override void ejectMoney()
17         {
18             Console.WriteLine("请先投币,再退币(不正确的操作)");
19         }
20 
21         public override void insertMoney()
22         {
23             Console.WriteLine("糖果已售罄,不要再投币(不正确的操作)");
24         }
25 
26         public override void turnCrank()
27         {
28             Console.WriteLine("请先投币,再转动把手(不正确的操作)");
29         }
30     }
售罄

  要注意这四个状态都要继承自State抽象类,它们的构造函数中均需要传入GumballMachine,它们各个动作会使糖果机处于不同的状态。(例如NoMoneyState(未投币)状态下,执行insertMoney动作后,糖果机状态转换为HasMoneyState(已投币状态))。不同状态中存在一些无效的操作,这里我们直接打印了一句话,这里根据使用场景具体分析、处理。

  接下来我们来运行一下=》我们初始化一个糖果机,放入100颗糖果。

  接下来,然我们来看下状态模式的类图。Context上下文中存有一个State的超类,Context将不同的行为委托给具体的状态(没有投币,已经投币,发放(出售)糖果,糖果售罄来实现;State封装Context的一组特定行为。具体的状态根据当前环境以实现不同的效果。

  

 

  这样做的好处是将State之间的逻辑解耦。例如,我们现在要再添加一个状态:幸运者!这样我们的系统就变成这样,在投币之后,我们将有1/10的概率成为幸运用户,这时候糖果机将给你两颗糖果。此时,我们添加一个状态LuckyState(幸运用户)。

 

 1     public class LuckyState : State            //是否是幸运
 2     {
 3         private readonly GumballMachine _GumballMachine;
 4         public LuckyState(GumballMachine GumballMachine)
 5         {
 6             _GumballMachine = GumballMachine;
 7         }
 8 
 9         public override void dispense()     
10         {
11             Console.WriteLine("你是幸运者!接下来我将给你两颗糖");
12             _GumballMachine.releaseBall();
13             if (_GumballMachine.GetCount() == 0)                            //糖果售罄
14                 _GumballMachine.setState(new SoldOutState(_GumballMachine));
15             else
16             {
17                 _GumballMachine.releaseBall();                              //给出第二颗糖果
18                 if (_GumballMachine.GetCount() > 0)
19                     _GumballMachine.setState(new NoMoneyState(_GumballMachine));    //还有糖果,进入未投币状态
20                 else
21                     _GumballMachine.setState(new SoldOutState(_GumballMachine));
22 
23             }
24         }
25 
26         public override void ejectMoney()
27         {
28             Console.WriteLine("退币");
29             _GumballMachine.setState(new NoMoneyState(_GumballMachine));
30         }
31 
32         public override void insertMoney()
33         {
34             Console.WriteLine("已投币,不需要再次投币(不正确的操作)");
35         }
36 
37         public override void turnCrank()
38         {
39             Console.WriteLine("你是幸运者,不需要摇动把手了,直接给你两颗糖(不正确的操作)");
40         }
41     }
幸运用户

  我们还要修改我们的HasMoneyState,当成为幸运用户的时候,糖果机变为另外一个状态。我们来执行下=》

   这样我们就很容易的通过添加状态类来扩展我们的糖果机。

最后总结一下:

  例子中的糖果机(GumballMachine)就是我们的上下文对象,然后我们的不同的状态变化时候,糖果机(GumballMachine)就会拥有不同的状态,不同的状态下会使GumballMachine有不同的行为(这样看来我们就把我们的Context上下文对象委托给了当前的状态对象),我们每一个状态都有一个单独的类,这些状态类都继承自State抽象类,所以我们可以随意的替换它们。我们要扩展一个状态也只需要添加一个继承自State的状态类。

  同时,这样做的不好处就是我们的状态类会变得很多。

posted @ 2018-01-15 14:13  3WLineCode  阅读(312)  评论(0编辑  收藏  举报