• 12 状态模式


    状态模式(State)定义:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

    UML类图

    状态模式

    状态模式适用于要转换很多业务状态的场景。比如,Head First举的糖果机例子,

    搜狗截图20161218071652

    糖果机有“没有投币”、“有投币”、“售出糖果”、“糖果售罄”四种状态,并且有四种动作:“投入硬币”、“返回硬币”、“转动曲柄”、“发放糖果”。四种动作与四种状态之间的关系比较复杂,但上图可以清晰地表达出这种关系。

    如果按照面向过程的实现方法,要表达出这种关系,需要一大堆的if else分支(4*4=16个),更麻烦的是,如果有新的状态加入,要修改的地方太多,违背了开放-封闭原则。

    而采用状态模式可以部分地解决这个问题。

    状态模式将每个状态封装成一个类,四种动作便成了类的四个方法,不同的类对这些方法有不同的实现,实现了状态类之间的自动切换。看起来很有《失控》中,那只用去中心化思维制造的机器昆虫,没有大脑,只为每只脚设定了动作规则,就可以完美运行。

    代码如下:

    先是规定了四种动作的接口:

    1     interface State
    2     {
    3         void InsertQuarter();
    4         void EjectQuarter();
    5         void TurnQuarter();
    6         void Dispense();
    7     }
    View Code

    自带逻辑的四种状态如下(五种状态,这里多了赢家状态,赢家状态会一次吐出两颗糖果):

      1     class NoQuarterState:State
      2     {
      3         GumballMachine gumballMachine;
      4         public NoQuarterState(GumballMachine g)
      5         {
      6             gumballMachine = g;
      7         }
      8         public void InsertQuarter()
      9         {
     10             Console.WriteLine("You inserted a quarter");
     11             gumballMachine.SetState(gumballMachine.GetHasQuarterState());
     12         }
     13 
     14         public void EjectQuarter()
     15         {
     16             Console.WriteLine("You haven't inserted a quarter");
     17         }
     18 
     19         public void TurnQuarter()
     20         {
     21             Console.WriteLine("You turned,but there's no quarter");
     22         }
     23 
     24         public void Dispense()
     25         {
     26             Console.WriteLine("You need to pay first");
     27         }
     28     }
     29 
     30 //
     31 class HasQuarterState : State
     32     {
     33         Random randomWinner = new Random(DateTime.Now.Millisecond);
     34         GumballMachine gumballMachine;
     35         public HasQuarterState(GumballMachine g)
     36         {
     37             gumballMachine = g;
     38         }
     39         public void InsertQuarter()
     40         {
     41             Console.WriteLine("You can't insert another quarter");
     42         }
     43 
     44         public void EjectQuarter()
     45         {
     46             Console.WriteLine("Quarter returned");
     47         }
     48 
     49         public void TurnQuarter()
     50         {
     51 
     52             Console.WriteLine("You turned");
     53             int randomNum = randomWinner.Next();
     54             if ((randomNum % 2 == 0) && (gumballMachine.GetCurrentCount() > 0))
     55             {
     56                 gumballMachine.SetState(gumballMachine.GetWinnerState());
     57             }
     58             else
     59             {
     60                 gumballMachine.SetState(gumballMachine.GetSoldState());
     61             }
     62             
     63         }
     64 
     65         public void Dispense()
     66         {
     67             Console.WriteLine("No gumball dispensed");
     68         }
     69     }
     70 //
     71 class SoldState : State
     72     {
     73         GumballMachine gumballMachine;
     74         public SoldState(GumballMachine g)
     75         {
     76             gumballMachine = g;
     77         }
     78         public void InsertQuarter()
     79         {
     80             Console.WriteLine("Please wait,we're already giving you a gumball");
     81         }
     82 
     83         public void EjectQuarter()
     84         {
     85             Console.WriteLine("Sorry, you already turned the crank");
     86         }
     87 
     88         public void TurnQuarter()
     89         {
     90             Console.WriteLine("Turning twice doesn't get you another gumball");
     91         }
     92 
     93         public void Dispense()
     94         {
     95             gumballMachine.ReleaseBall();
     96             if (gumballMachine.GetCurrentCount() > 0)
     97             {
     98                 gumballMachine.SetState(gumballMachine.GetNoQuarterState());
     99             }
    100             else
    101             {
    102                 Console.WriteLine("Oops,out of gumballs");
    103                 gumballMachine.SetState(gumballMachine.GetSoldOutState());
    104             }
    105         }
    106     }
    107 //
    108 class SoldOutState:State
    109     {
    110         GumballMachine gumballMachine;
    111         public SoldOutState(GumballMachine g)
    112         {
    113             gumballMachine = g;
    114         }
    115         public void InsertQuarter()
    116         {
    117             Console.WriteLine("There is not gumballs");
    118         }
    119 
    120         public void EjectQuarter()
    121         {
    122             Console.WriteLine("You haven't inject a gumball");
    123         }
    124 
    125         public void TurnQuarter()
    126         {
    127             Console.WriteLine("Be quiet");
    128         }
    129 
    130         public void Dispense()
    131         {
    132             Console.WriteLine("No quarter anymore");
    133         }
    134     }
    View Code

    糖果机代码如下:

     1 class GumballMachine
     2     {
     3         State soldOutState;
     4         State noQuarterState;
     5         State hasQuarterState;
     6         State soldState;
     7         State winnerState;
     8         State state;
     9         int count = 0;
    10         public GumballMachine(int numberGumballs)
    11         {
    12             soldOutState = new SoldOutState(this);
    13             noQuarterState = new NoQuarterState(this);
    14             hasQuarterState = new HasQuarterState(this);
    15             soldState = new SoldState(this);
    16             winnerState = new WinnerState(this);
    17             this.count = numberGumballs;
    18             if (numberGumballs > 0)
    19             {
    20                 state = noQuarterState;
    21             }
    22             else
    23             {
    24                 state = soldOutState;
    25             }
    26         }
    27 
    28         public void InsertQuarter()
    29         {
    30             state.InsertQuarter();
    31         }
    32         public void EjectQuarter()
    33         {
    34             state.EjectQuarter();
    35         }
    36         public void TurnCrank()
    37         {
    38             state.TurnQuarter();
    39             state.Dispense();
    40         }
    41         public void SetState(State s)
    42         {
    43             state = s;
    44         }
    45         public void ReleaseBall()
    46         {
    47             Console.WriteLine("A gumball comes rolling out the slot");
    48             if (count != 0) count--;
    49         }
    50 
    51         public State GetSoldOutState()
    52         {
    53             return soldOutState;
    54         }
    55         public State GetNoQuarterState()
    56         {
    57             return noQuarterState;
    58         }
    59         public State GetHasQuarterState()
    60         {
    61             return hasQuarterState;
    62         }
    63         public State GetSoldState()
    64         {
    65             return soldState;
    66         }
    67         public State GetWinnerState()
    68         {
    69             return winnerState;
    70         }
    71         public int GetCurrentCount()
    72         {
    73             return count;
    74         }
    75     }
    View Code

    这里状态的转换由State类内部控制,但有可由Context就是这儿的Main函数中直接控制,可根据具体需求决定采用哪种方式。

    使用状态模式通常会导致设计中类的数目大量增加。但相比很多if else分支来说,还是可取的,而且这些类并不对外可见。

    状态模式与策略模式的区别:

    两者的类图相同,但区别在于意图不同。

    状态模式允许Context随着状态的改变而改变行为,但策略模式则用行为或算法来配置Context类。状态模式的状态变化逻辑已经内化,但策略模式使用哪种算法,可由外部决定。

  • 相关阅读:
    eclipse在线安装mybatis generator插件、及插件的使用
    Oracle 的SID 与 Service_Name 区别
    Mybatis generator 配置报错
    Mybatis generator 生成Javabean报错:Table configuration with catalog null, schema public, and table globalpage did not resolve to any tables
    pom.xml中添加oracle数据库驱动包报错: Missing artifact com.oracle:ojdbc14:jar:10.2.0.4.0
    Eclipse中10个最有用的快捷键组合
    Spring Tool Suite 配置和使用
    【搜索】【动态规划】【杂题】——洛谷P1514.引水入城
    c++ 矩阵求逆
    LNK1104 无法打开文件 exe
  • 原文地址:https://www.cnblogs.com/zhixin9001/p/6193705.html
Copyright © 2020-2023  润新知