【设计模式】学习笔记14:状态模式(State)

来源:互联网 发布:procedure执行sql语句 编辑:程序博客网 时间:2024/06/08 20:03

本文出自   http://blog.csdn.net/shuangde800



基本常识: 策略模式和状态模式是双胞胎,在出生时才分开


认识状态模式

假设有一个糖果机, 它的工作状态图如下:



要用代码实现糖果机的功能, 如果不用状态模式:

一种方法是创建一个类,它的作用就是一个状态机,对每一个动作,我们都创建了一个对应的方法,这些方法用条件语句来决定在每一个状态内什么方法是最恰当的.比如对"投入25分钱"这个动作,对应的方法如下:

[java] view plaincopy
  1. // 我们根据糖果机的状态,定义4种状态,用整形常量来表示  
  2. final static int SOLD_OUT= 0// 糖果卖完了  
  3. final static int NO_QUARTER = 1// 没有投钱  
  4. final static int HAS_QUARTER = 2;  // 已投钱了  
  5. final static int SOLD = 3// 正在出售糖果  
  6. public void insertQurter() {  
  7.       
  8.     // 根据不同的状态,会有不同的动作反应  
  9.     if (state == HAS_QUARTER) {  
  10.         // do something       
  11.     } else  if (state == SOLD_OUT) {  
  12.         // do something       
  13.     } else if (state == SOLD) {  
  14.         // do something       
  15.     } else if (state == NO_QUARTER) {  
  16.         // do something       
  17.     }  
  18. }  


这样做的缺点:
会产生大量的if...else语句, 代码将很不容易改变, 难以拓展.

没有遵守"开放-关闭"原则

不符合面向对象

状态转换隐藏在条件语句中,所以并不明显

没有把会改变的部分包装起来

未来加入的代码可能导致bug



是时候学习新的设计模式了


定义状态模式

状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类


因为这个模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象,我们知道行为会随着内部状态而改变。


Contex(上下文):是一个类,它可以拥有一些内部状态。

State接口:定义了一个所有具体状态的共同接口;任何状态都实现这个相同的接口,这样一来,状态之间可以互相替换

ConcreteState(具体状态): 处理来自Context的请求.每个ConcreteState都提供了它自己对于请求的实现.所以,当Context改变状态时行为也跟着改变




状态模式的类图和策略模式的基本一模一样, 但它们的"意图"不一样

状态模式允许Context随着状态的改变而改变行为.

策略模式通常会用行为或算法来配置Context类. 通常某个Context类只有一个最适当的策略.


使用状态模式通常那个会导致设计中的类数目大量增加. 因为"一个类,一个责任"原则



用状态模式实现糖果机

状态类图:


1. 实现State接口

[java] view plaincopy
  1. public interface State {  
  2.    
  3.     // 封装四种动作  
  4.     public void insertQuarter(); // 投币  
  5.     public void ejectQuarter();  // 退币  
  6.     public void turnCrank();     // 转动摇柄  
  7.     public void dispense();      // 发糖果  
  8. }  

2. 实现具体的状态

[java] view plaincopy
  1. // 没有投币的状态  
  2. public class NoQuarterState implements State {  
  3.     GumballMachine gumballMachine;  
  4.    
  5.     public NoQuarterState(GumballMachine gumballMachine) {  
  6.         this.gumballMachine = gumballMachine;  
  7.     }  
  8.    
  9.     public void insertQuarter() {  
  10.         System.out.println("You inserted a quarter");  
  11.         gumballMachine.setState(gumballMachine.getHasQuarterState());  
  12.     }  
  13.    
  14.     public void ejectQuarter() {  
  15.         System.out.println("You haven't inserted a quarter");  
  16.     }  
  17.    
  18.     public void turnCrank() {  
  19.         System.out.println("You turned, but there's no quarter");  
  20.      }  
  21.    
  22.     public void dispense() {  
  23.         System.out.println("You need to pay first");  
  24.     }   
  25.    
  26.     public String toString() {  
  27.         return "waiting for quarter";  
  28.     }  
  29. }  

[java] view plaincopy
  1. // 已经投币的状态  
  2. public class HasQuarterState implements State {  
  3.     GumballMachine gumballMachine;  
  4.    
  5.     public HasQuarterState(GumballMachine gumballMachine) {  
  6.         this.gumballMachine = gumballMachine;  
  7.     }  
  8.     
  9.     public void insertQuarter() {  
  10.         System.out.println("You can't insert another quarter");  
  11.     }  
  12.    
  13.     public void ejectQuarter() {  
  14.         System.out.println("Quarter returned");  
  15.         gumballMachine.setState(gumballMachine.getNoQuarterState());  
  16.     }  
  17.    
  18.     public void turnCrank() {  
  19.         System.out.println("You turned...");  
  20.         gumballMachine.setState(gumballMachine.getSoldState());  
  21.     }  
  22.   
  23.     public void dispense() {  
  24.         System.out.println("No gumball dispensed");  
  25.     }  
  26.    
  27.     public String toString() {  
  28.         return "waiting for turn of crank";  
  29.     }  
  30. }  

[java] view plaincopy
  1. // 正在售出的状态  
  2. public class SoldState implements State {  
  3.    
  4.     GumballMachine gumballMachine;  
  5.    
  6.     public SoldState(GumballMachine gumballMachine) {  
  7.         this.gumballMachine = gumballMachine;  
  8.     }  
  9.          
  10.     public void insertQuarter() {  
  11.         System.out.println("Please wait, we're already giving you a gumball");  
  12.     }  
  13.    
  14.     public void ejectQuarter() {  
  15.         System.out.println("Sorry, you already turned the crank");  
  16.     }  
  17.    
  18.     public void turnCrank() {  
  19.         System.out.println("Turning twice doesn't get you another gumball!");  
  20.     }  
  21.    
  22.     public void dispense() {  
  23.         gumballMachine.releaseBall();  
  24.         if (gumballMachine.getCount() > 0) {  
  25.             gumballMachine.setState(gumballMachine.getNoQuarterState());  
  26.         } else {  
  27.             System.out.println("Oops, out of gumballs!");  
  28.             gumballMachine.setState(gumballMachine.getSoldOutState());  
  29.         }  
  30.     }  
  31.    
  32.     public String toString() {  
  33.         return "dispensing a gumball";  
  34.     }  
  35. }  

.....

3. 实现Context

[java] view plaincopy
  1. public class GumballMachine {  
  2.    
  3.     State soldOutState;  
  4.     State noQuarterState;  
  5.     State hasQuarterState;  
  6.     State soldState;  
  7.    
  8.     State state = soldOutState;  
  9.     int count = 0;  
  10.    
  11.     public GumballMachine(int numberGumballs) {  
  12.         soldOutState = new SoldOutState(this);  
  13.         noQuarterState = new NoQuarterState(this);  
  14.         hasQuarterState = new HasQuarterState(this);  
  15.         soldState = new SoldState(this);  
  16.   
  17.         this.count = numberGumballs;  
  18.         if (numberGumballs > 0) {  
  19.             state = noQuarterState;  
  20.         }   
  21.     }  
  22.    
  23.     public void insertQuarter() {  
  24.         state.insertQuarter();  
  25.     }  
  26.    
  27.     public void ejectQuarter() {  
  28.         state.ejectQuarter();  
  29.     }  
  30.    
  31.     public void turnCrank() {  
  32.         state.turnCrank();  
  33.         state.dispense();  
  34.     }  
  35.   
  36.     void setState(State state) {  
  37.         this.state = state;  
  38.     }  
  39.    
  40.     void releaseBall() {  
  41.         System.out.println("A gumball comes rolling out the slot...");  
  42.         if (count != 0) {  
  43.             count = count - 1;  
  44.         }  
  45.     }  
  46.    
  47.     int getCount() {  
  48.         return count;  
  49.     }  
  50.    
  51.     void refill(int count) {  
  52.         this.count = count;  
  53.         state = noQuarterState;  
  54.     }  
  55.   
  56.     public State getState() {  
  57.         return state;  
  58.     }  
  59.   
  60.     public State getSoldOutState() {  
  61.         return soldOutState;  
  62.     }  
  63.   
  64.     public State getNoQuarterState() {  
  65.         return noQuarterState;  
  66.     }  
  67.   
  68.     public State getHasQuarterState() {  
  69.         return hasQuarterState;  
  70.     }  
  71.   
  72.     public State getSoldState() {  
  73.         return soldState;  
  74.     }  
  75.    
  76.     public String toString() {  
  77.         StringBuffer result = new StringBuffer();  
  78.         result.append("\nMighty Gumball, Inc.");  
  79.         result.append("\nJava-enabled Standing Gumball Model #2004");  
  80.         result.append("\nInventory: " + count + " gumball");  
  81.         if (count != 1) {  
  82.             result.append("s");  
  83.         }  
  84.         result.append("\n");  
  85.         result.append("Machine is " + state + "\n");  
  86.         return result.toString();  
  87.     }  
  88. }  


4. 测试类
[java] view plaincopy
  1. public class GumballMachineTestDrive {  
  2.   
  3.     public static void main(String[] args) {  
  4.         GumballMachine gumballMachine = new GumballMachine(5);  
  5.   
  6.         System.out.println(gumballMachine);  
  7.   
  8.         gumballMachine.insertQuarter();  
  9.         gumballMachine.turnCrank();  
  10.   
  11.         System.out.println(gumballMachine);  
  12.   
  13.         gumballMachine.insertQuarter();  
  14.         gumballMachine.turnCrank();  
  15.         gumballMachine.insertQuarter();  
  16.         gumballMachine.turnCrank();  
  17.   
  18.         System.out.println(gumballMachine);  
  19.     }  
  20. }  

这样做的优点:

1. 将每个状态的行为局部化到自己的类中

2. 将容易产生问题的if语句删除,以方便日后的维护

3. 让每个状态"对修改关闭",让糖果机"对拓展开放",因为可以加入新的状态类

4. 创建一个新的代码基和类结构,这更能映射糖果的图,而且容易阅读和理解

缺点:

在状态多如牛毛的时候,会产生类泛滥,故而每一个设计模式都只是适用于相对应的情况下,具体视情况而定,不要死记硬背,要灵活运用;要将具体抽象出来,适用这些所谓的设计模式,也需要经验的积累,充分的思考+时间,才能捣鼓出一个良好的架构出来。。。。

适用:

1.状态模式是用在状态不是非常多的情况
2.之所以用设计模式是为了能够封装变化。也就是说状态模式,是在你的状态可能增加,可能会改变的时候,可以复用原本的代码,且不用对以前的代码进行修改(所谓的开闭原则)。在你的状态几乎不变的情况下,可以不用状态模式
3.你如果使用状态模式,可以把状态的转化,用图画出来。别人可以更好理解的你代码