状态模式:允许对象的内部状态改变时改变它的行为,对想看起来好像修改了它的类。
      状态模式和策略模式比较相似(类图就完全一样),都是A对象关联B对象,而B是可以更换的,A对象的功能随着B的具体实现的改变而改变。策略模式一般都是B对象在A对象所在的上下文中改变。而状态模式则是在B对象(状态对象)完成一定的操作在B对象内部吧A关联的B的具体实现更换。对于A来说就像是一个封闭的机器,内部实现了一套状态转换的流程而无需外界的干涉。
下面是一个糖果机的例子。
先讨论四种状态,有25分钱,没有25分钱,售出,售罄。然后扩展一个赢家状态。
基本方法有 插入硬币,退出货币,摇动手柄,发出糖果。

 class GumballMachine
    
{
        
int count = 5;

        
public int Count
        
{
            
get return count; }
            
set { count = value; }
        }

        
public State state;
        
public GumballMachine()
        
{
            state 
= new NoQuarterState(this);
        }

        
public void InsertQuarter()
        
{
            state.InsertQuarter();
        }

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

        
public void TrunCrank()
        
{
            state.TrunCrank();
        }

        
public void Dispense()
        
{
            state.Dispense();
        }

    }

糖果机的行为都委托给状态对象去实现。随着状态(对象)的改变糖果机的行为也会改变,也就是说糖果机在不同的状态下表现出不同的行为。


 abstract class State
    
{
       
protected GumballMachine gumballmachine;
        
public virtual void InsertQuarter()
        
{
        }

        
public  virtual void EjectQuarter()
        
{
            Console.WriteLine(
"\t没糖了,钱退给你!");
        }

        
public virtual void TrunCrank()
        
{
        }

        
public virtual void Dispense()
        
{
        }

    }
四个方法在状态基类中都提供默认实现。

 class NoQuarterState:State
    
{
        
public NoQuarterState( GumballMachine gumballmachine)
        
{
            
this.gumballmachine = gumballmachine;
        }

       
        
public override void InsertQuarter()
        
{
            
if (gumballmachine.Count < 1)
            
{
                gumballmachine.state 
= new SoldOutState(gumballmachine);
                gumballmachine.state.EjectQuarter();
            }

            
else
            
{
                gumballmachine.state 
= new HasQuarterState(gumballmachine);
            }

        }

    }
    在没有25分钱的状态下,插入硬币,如果有糖果则糖果机变成有25分钱状态。没有糖果则变成售罄状态然后把钱退回。(为什么售罄状态不在首先就确定而要插入硬币后才确定呢(1?))
 class HasQuarterState : State
    
{
        Random ran 
= new Random();
        
public HasQuarterState(GumballMachine gumballmachine)
        
{
            
this.gumballmachine = gumballmachine;
        }

        
public override void TrunCrank()
        
{

            
if (gumballmachine.Count > 0)
            
{
                
if (ran.Next(10== 5)
                
{
                    gumballmachine.state 
= new WinnerState(gumballmachine);
                }

                
else
                
{
                    gumballmachine.state 
= new SoldState(gumballmachine);
                }

                gumballmachine.state.Dispense();
            }

            
else
            
{
                gumballmachine.state 
= new SoldOutState(gumballmachine);
                gumballmachine.state.EjectQuarter();
            }


        }


    }
有25分钱状态下转动手柄,马上变成售出状态。
 class SoldOutState:State
    
{
        
public SoldOutState(GumballMachine gumballmachine)
        
{
            
this.gumballmachine = gumballmachine;
        }

        
public override void InsertQuarter()
        
{
            EjectQuarter();
        }

    }
售罄状态,插入钱之后马上退出钱。和初始状态下没有糖果的糖果机保持一致。
 public SoldState(GumballMachine gumballmachine)
        
{
            
this.gumballmachine = gumballmachine;
        }

        
public override void Dispense()
        
{
            Console.WriteLine(
"Give you a candy!");
            
if (gumballmachine.Count > 0)
            
{
                gumballmachine.Count
--
                gumballmachine.state 
= new NoQuarterState(gumballmachine);
            }

            
else
            
{
                gumballmachine.state 
= new SoldOutState(gumballmachine);
            }

        }

    }
售出状态自然就要发放糖果咯。
 class WinnerState : State
    
{
        
public WinnerState( GumballMachine gumballmachine)
        
{
            
this.gumballmachine = gumballmachine;
        }

        
public override void Dispense()
        
{
            Console.WriteLine(
"you are winner");

                
for (int i = 0; i < 2; i++)
                
{
                    
if (gumballmachine.Count > 0)
                    
{
                        Console.WriteLine(
"Give you a candy!");
                        gumballmachine.Count
--;
                    }

                }



                gumballmachine.state 
= new NoQuarterState(gumballmachine);
        }

    }
为了增加糖果机的趣味性,运营商希望增加一个功能,顾客有1/10的机会获得两颗糖果。那么我们很轻松的扩展一个状态:赢家状态。
 static void Main(string[] args)
        
{
            GumballMachine gum 
= new GumballMachine();
            
for (int i = 0; i < 8; i++)
            
{
                gum.InsertQuarter();
                gum.TrunCrank();
                Console.WriteLine(gum.Count);
            }

            Console.ReadLine();
        }
测试代码。

(1?)上面已经提到糖果机内部实现了一套状态转换的流程而无需外界的干涉,那么售罄状态自然也会从某一状态而来,又转向某一状态而去。具体来说就是在售出状态下发放糖果后马上检查还有没有糖果,没有糖果则转到售罄状态,难道这不是售罄状态的所有“来路”吗?在正常状况下自然是这样。我们在初始化糖果机的时候会给出一个初始状态--没有25分钱状态(貌似这样还算比较合理)。但是如果糖果机中一开始就没有糖果那他本身又是一种售罄状态。同时它还没有钱嘛。所以就在插入硬币的时候判断咯,没有糖果就转到售罄然后退出钱。

这里的状态才看起来是有点犯晕。因为这些状态不是互斥的状态。也就是说这里的状态划分不是一种不重不漏的方式。在有钱状态下同时还可以是售罄,没钱也可以是售罄。有25分钱,没有25分钱,表示有没有钱,售罄,表示还有没有糖果,售出表示糖果机的正要进行的动作。表示3类东西的状态竟然放在一起来相互转换 orz,orz。居然还算perfect。懂得模式很重要,发现模式更重要啊。在这个例子中就算想到了状态模式也会由于分析不出正确的状态,而被自己否决吧。完了,感谢CCTV ,感谢BTV ,感谢博客堂,最后最重要的是感谢《head first design pattern》这个例子就是来自与它。