状态模式:允许对象在内部状态改变时,改变它的行为,对象看起来好像修改了它的类。
状态模式将一个类的实例对象的状态封装成为独立的类,并把类的动作委托到代表当前状态的类去实现。这样当对象的状态发生改变时,这个对象就会呈现不同的动作表现。
类图:
糖果售卖机具有四种状态:没有25分钱状态,有25分钱状态,售出糖果状态,糖果售罄状态
在糖果售卖机处于不同的状态下,糖果售卖机的动作表现不同。如在没有25分钱的状态下,投入25分钱的动作是合法的,在售出糖果的状态下,投入25分钱的动作是不合法的。
这时,就要更具售卖机对象处于不同的状态,来输出不同的语句。
状态接口 State接口
1 /** 2 * 这是糖果售卖机的状态接口 State 3 * 糖果售卖机具有四种状态:没有25分钱状态,有25分钱状态,售出糖果状态,糖果售罄状态 4 * 糖果售卖机 提供三个接口 给顾客调用 :投入25分钱,退出25分钱,转动曲柄(相当于确认购买) 5 * 糖果售卖机 有一个接口 给自己调用:发放糖果 6 * @author wly 7 * 8 */ 9 public interface State { 10 /** 11 * 投入25分钱 的方法 12 */ 13 public void insertQuarter(); 14 /** 15 * 退出25分钱 的方法 16 */ 17 public void ejectQuarter(); 18 /** 19 * 转动曲柄 确认购买 的方法 20 */ 21 public void turnCrank(); 22 /** 23 * 发放糖果 的方法 24 */ 25 public void dispense(); 26 }
糖果售卖机 GumballMachine类
1 /** 2 * 这是糖果售卖机 GumballMachine类 3 * @author wly 4 * 5 */ 6 public class GumballMachine { 7 //声明一个变量,来引用 没有25分钱状态对象 8 private NoQuarterState noQuarterState; 9 //声明一个变量,来引用 有25分钱状态对象 10 private HasQuarterState hasQuarterState; 11 //声明一个变量,来引用 售卖状态对象 12 private SoldState soldState; 13 //声明一个变量,来引用 售罄状态对象 14 private SoldOutState soldOutState; 15 //声明一个变量 , 来表示 当前糖果售卖机 是处于 哪一种状态 新的糖果机处于售罄状态(因为没有糖果) 16 private State state = soldOutState; 17 //声明一个变量 , 表示 糖果的剩余数量 18 private int count; 19 /** 20 * 糖果售卖机的构造器 21 * @param count 22 */ 23 public GumballMachine(int count) 24 { 25 //为糖果售卖机 创建四种状态对象 并把糖果售卖机的对象引用传给状态对象 26 this.noQuarterState = new NoQuarterState(this); 27 this.hasQuarterState = new HasQuarterState(this); 28 this.soldState = new SoldState(this); 29 this.soldOutState = new SoldOutState(this); 30 this.count = count; 31 //如果 传入的糖果数量大于0 让糖果售卖机 到 没有25分钱的状态 32 if(count > 0 ) 33 { 34 this.setState(noQuarterState); 35 } 36 } 37 /** 38 * 用来设置糖果售卖机当前状态对象的 setter方法 39 * @param state 40 */ 41 public void setState(State state) 42 { 43 this.state = state; 44 } 45 /** 46 * 发出一个糖果 ,糖果剩余数量减1 47 */ 48 public void realseBall() 49 { 50 if(count > 0) 51 { 52 count--; 53 } 54 System.out.println("一个糖果已经发出...还剩"+count+"个糖果"); 55 } 56 //放入25分钱 的方法 让已经持有糖果机对象的状态对象去处理 57 public void insertQuarter() { 58 state.insertQuarter(); 59 } 60 //退回25分钱 的方法 让已经持有糖果机对象的状态对象去处理 61 public void ejectQuarter() { 62 state.ejectQuarter(); 63 } 64 //转动曲柄 的方法 让已经持有糖果机对象的状态对象去处理 65 public void turnCrank() { 66 state.turnCrank(); 67 //现在糖果机处于售卖状态 会自动调用dispense()方法 来发放糖果 68 state.dispense(); 69 } 70 //以下是 四种状态对象 和 糖果剩余数量对象的 getter方法 和 setter方法 71 public NoQuarterState getNoQuarterState() { 72 return noQuarterState; 73 } 74 public void setNoQuarterState(NoQuarterState noQuarterState) { 75 this.noQuarterState = noQuarterState; 76 } 77 public HasQuarterState getHasQuarterState() { 78 return hasQuarterState; 79 } 80 public void setHasQuarterState(HasQuarterState hasQuarterState) { 81 this.hasQuarterState = hasQuarterState; 82 } 83 public SoldState getSoldState() { 84 return soldState; 85 } 86 public void setSoldState(SoldState soldState) { 87 this.soldState = soldState; 88 } 89 public SoldOutState getSoldOutState() { 90 return soldOutState; 91 } 92 public void setSoldOutState(SoldOutState soldOutState) { 93 this.soldOutState = soldOutState; 94 } 95 public int getCount() { 96 return count; 97 } 98 public void setCount(int count) { 99 this.count = count; 100 } 101 102 }
没有25分钱状态 NoQuarterState类
1 /** 2 * 这是没有25分钱状态 NoQuarterState类 3 * 没有25分钱状态下 只有 放入钱的方法可用 可以引起状态改变 4 * @author wly 5 * 6 */ 7 public class NoQuarterState implements State { 8 //声明一个变量 ,来引用糖果售卖机对象 9 private GumballMachine gm; 10 public NoQuarterState(GumballMachine gm) 11 { 12 this.gm = gm; 13 } 14 @Override 15 public void insertQuarter() { 16 System.out.println("你已经放入了25分钱..."); 17 //改变糖果售卖机的状态 到 有25分钱的状态 18 gm.setState(gm.getHasQuarterState()); 19 } 20 21 @Override 22 public void ejectQuarter() { 23 System.out.println("你还没有放入钱,不能要求退钱..."); 24 } 25 26 @Override 27 public void turnCrank() { 28 System.out.println("你还没有放入钱,不能转动曲柄..."); 29 } 30 31 @Override 32 public void dispense() { 33 System.out.println("你还没有放入钱,不能发放糖果..."); 34 } 35 36 }
有25分钱的状态 HasQuarterState类
1 /** 2 * 这是有25分钱的状态 HasQuarterState类 3 * 在有25分钱的状态下 可以使用 退回钱的方法 从而让糖果售卖机的状态回到 没有25分钱状态 4 * 可以使用 转动曲柄的方法 让糖果售卖机的状态前进到 售卖状态 5 * @author wly 6 * 7 */ 8 public class HasQuarterState implements State { 9 //声明一个变量 ,来引用糖果售卖机对象 10 private GumballMachine gm; 11 public HasQuarterState(GumballMachine gm) 12 { 13 this.gm = gm; 14 } 15 @Override 16 public void insertQuarter() { 17 System.out.println("你已经放入了钱,不能再放入钱..."); 18 } 19 20 @Override 21 public void ejectQuarter() { 22 System.out.println("钱将退回给你..."); 23 //改变糖果售卖机的状态 到 没有25分钱的状态 24 gm.setState(gm.getNoQuarterState()); 25 } 26 27 @Override 28 public void turnCrank() { 29 System.out.println("转动曲柄..."); 30 //改变糖果售卖机的状态 到 售卖的状态 31 gm.setState(gm.getSoldState()); 32 } 33 34 @Override 35 public void dispense() { 36 System.out.println("糖果暂时不能发放..."); 37 } 38 39 }
售卖的状态 SoldState类
1 /** 2 * 这是售卖的状态 SoldState类 3 * 售卖状态的 糖果发放方法 调用之后 根据当前的糖果数量 来决定 到到哪个状态 4 * @author wly 5 * 6 */ 7 public class SoldState implements State { 8 //声明一个变量 ,来引用糖果售卖机对象 9 private GumballMachine gm; 10 public SoldState(GumballMachine gm) 11 { 12 this.gm = gm; 13 } 14 15 @Override 16 public void insertQuarter() { 17 System.out.println("请稍等,上一次的购买还在发放中,现在不能放入钱..."); 18 } 19 20 @Override 21 public void ejectQuarter() { 22 System.out.println("你已经转动曲柄,确认购买了,不能退钱了..."); 23 } 24 25 @Override 26 public void turnCrank() { 27 System.out.println("曲柄已经转动了,不能转动两次..."); 28 } 29 30 @Override 31 public void dispense() { 32 System.out.println("发放糖果中..."); 33 //发放糖果的方法,糖果数量减1 34 gm.realseBall(); 35 //如果糖果售卖机中的 糖果数量 大于0 糖果售卖机的状态 到 没有25分钱的状态 36 if(gm.getCount() > 0) 37 { 38 gm.setState(gm.getNoQuarterState()); 39 } 40 else //如果糖果售卖机中的 糖果数量 小于等于0 糖果售卖机的状态 到 售罄状态 41 { 42 System.out.println("糖果已经售罄..."); 43 gm.setState(gm.getSoldOutState()); 44 } 45 } 46 47 }
糖果售罄状态 SoldOutState类
1 /** 2 * 这是糖果售罄状态 SoldOutState类 3 * @author wly 4 * 5 */ 6 public class SoldOutState implements State{ 7 //声明一个变量 ,来引用糖果售卖机对象 8 private GumballMachine gm; 9 public SoldOutState(GumballMachine gm) 10 { 11 this.gm = gm; 12 } 13 @Override 14 public void insertQuarter() { 15 System.out.println("糖果已经售罄,不能再放入钱..."); 16 } 17 18 @Override 19 public void ejectQuarter() { 20 System.out.println("糖果已经售罄,不能退回钱..."); 21 } 22 23 @Override 24 public void turnCrank() { 25 System.out.println("糖果已经售罄,不能转动曲柄..."); 26 } 27 28 @Override 29 public void dispense() { 30 System.out.println("糖果已经售罄,不能发放糖果..."); 31 } 32 }
最后提供一个测试类:
1 public class TestClass { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args) { 7 //创建一个糖果售卖机对象 ,并为这个糖果售卖机对象 放入5颗糖果 8 //糖果售卖机 处于 没有25分钱状态 9 GumballMachine gm = new GumballMachine(5); 10 //放入25分钱 , 糖果售卖机处于有25分钱状态 11 gm.insertQuarter(); 12 //转动曲柄 ,糖果售卖机处于售卖状态 ,自动调用发放糖果方法,糖果售卖机 将处于没有25分钱状态 13 gm.turnCrank(); 14 System.out.println("********************"); 15 //放入25分钱 , 糖果售卖机处于有25分钱状态 16 gm.insertQuarter(); 17 //退回25分钱 ,糖果售卖机处于没有25分钱状态 18 gm.ejectQuarter(); 19 System.out.println("********************"); 20 gm.insertQuarter(); 21 gm.turnCrank(); 22 System.out.println("********************"); 23 gm.insertQuarter(); 24 gm.turnCrank(); 25 System.out.println("********************"); 26 gm.insertQuarter(); 27 gm.turnCrank(); 28 System.out.println("********************"); 29 //放入25分钱 , 糖果售卖机处于有25分钱状态 30 gm.insertQuarter(); 31 //转动曲柄 ,糖果售卖机处于售卖状态 ,自动调用发放糖果方法,糖果售卖机 中的糖果数为0,糖果售卖机将到糖果售罄状态 32 gm.turnCrank(); 33 34 } 35 }
运行结果如下: