状态模式 有时一个对象的行为取决于一或多个动态变化的属性(状态),这样的对象称为有状态的(stateful)对象,其对象状态是从事先定义好的一系列值中取出。当这样的对象与外部事件产生互动时,内部状态就会改变,对象行为也随之变化。
在UML中可以使用状态图来描述对象状态的变化。在状态模式中,创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
架构
Context:环境类
State:抽象状态类
ConcreteState:具体状态类
意义 解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。 状态模式的关键是引入了一个抽象类来专门表示对象的状态 - 抽象状态类。而对象的每种具体状态类都继承该类,并在不同具体状态类中实现不同状态的行为,包括各种状态之间的转换。
使用状态模式定义糖果机 在现实世界中,有一台糖果机,可以糖果机上执行如下动作:
投入一枚25分钱的硬币(机器上有按钮)
退出一枚25分钱的硬币(机器上有按钮)
转动曲柄以获取糖果(机器上有曲柄)
糖果机释放糖果(糖果机在转动曲柄后,自动释放)
重新为糖果机装填糖果
糖果机的状态如下
定义糖果机的状态和接口
实现糖果机 GumballMachine 的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 package headfirst.designpatterns.state.gumballstate;public class GumballMachine { State soldOutState; State noQuarterState; State hasQuarterState; State soldState; State state; int count = 0 ; public GumballMachine (int numberGumballs) { soldOutState = new SoldOutState (this ); noQuarterState = new NoQuarterState (this ); hasQuarterState = new HasQuarterState (this ); soldState = new SoldState (this ); this .count = numberGumballs; if (numberGumballs > 0 ) { state = noQuarterState; } else { state = soldOutState; } } public void insertQuarter () { state.insertQuarter(); } public void ejectQuarter () { state.ejectQuarter(); } public void turnCrank () { state.turnCrank(); state.dispense(); } void releaseBall () { System.out.println("A gumball comes rolling out the slot..." ); if (count > 0 ) { count = count - 1 ; } } int getCount () { return count; } void refill (int count) { this .count += count; System.out.println("The gumball machine was just refilled; its new count is: " + this .count); state.refill(); } void setState (State state) { this .state = state; } public State getState () { return state; } public State getSoldOutState () { return soldOutState; } public State getNoQuarterState () { return noQuarterState; } public State getHasQuarterState () { return hasQuarterState; } public State getSoldState () { return soldState; } public String toString () { StringBuffer result = new StringBuffer (); result.append("\nMighty Gumball, Inc." ); result.append("\nJava-enabled Standing Gumball Model #2004" ); result.append("\nInventory: " + count + " gumball" ); if (count != 1 ) { result.append("s" ); } result.append("\n" ); result.append("Machine is " + state + "\n" ); return result.toString(); } }
定义 State 接口:
1 2 3 4 5 6 7 8 9 10 11 package headfirst.designpatterns.state.gumballstate;public interface State { void insertQuarter () ; void ejectQuarter () ; void turnCrank () ; void dispense () ; void refill () ; }
定义**”没有25分钱”** 的状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package headfirst.designpatterns.state.gumballstate;public class NoQuarterState implements State { GumballMachine gumballMachine; public NoQuarterState (GumballMachine gumballMachine) { this .gumballMachine = gumballMachine; } public void insertQuarter () { System.out.println("You inserted a quarter" ); gumballMachine.setState(gumballMachine.getHasQuarterState()); } public void ejectQuarter () { System.out.println("You haven't inserted a quarter" ); } public void turnCrank () { System.out.println("You turned, but there's no quarter" ); } public void dispense () { System.out.println("You need to pay first" ); } public void refill () { } public String toString () { return "waiting for quarter" ; } }
定义**”有25分钱”** 的状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package headfirst.designpatterns.state.gumballstate;public class HasQuarterState implements State { GumballMachine gumballMachine; public HasQuarterState (GumballMachine gumballMachine) { this .gumballMachine = gumballMachine; } public void insertQuarter () { System.out.println("You can't insert another quarter" ); } public void ejectQuarter () { System.out.println("Quarter returned" ); gumballMachine.setState(gumballMachine.getNoQuarterState()); } public void turnCrank () { System.out.println("You turned..." ); gumballMachine.setState(gumballMachine.getSoldState()); } public void dispense () { System.out.println("No gumball dispensed" ); } public void refill () { } public String toString () { return "waiting for turn of crank" ; } }
定义**”售出糖果”**的状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package headfirst.designpatterns.state.gumballstate;public class SoldState implements State { GumballMachine gumballMachine; public SoldState (GumballMachine gumballMachine) { this .gumballMachine = gumballMachine; } public void insertQuarter () { System.out.println("Please wait, we're already giving you a gumball" ); } public void ejectQuarter () { System.out.println("Sorry, you already turned the crank" ); } public void turnCrank () { System.out.println("Turning twice doesn't get you another gumball!" ); } public void dispense () { gumballMachine.releaseBall(); if (gumballMachine.getCount() > 0 ) { gumballMachine.setState(gumballMachine.getNoQuarterState()); } else { System.out.println("Oops, out of gumballs!" ); gumballMachine.setState(gumballMachine.getSoldOutState()); } } public void refill () { } public String toString () { return "dispensing a gumball" ; } }
定义**”售罄糖果”**的状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package headfirst.designpatterns.state.gumballstate;public class SoldOutState implements State { GumballMachine gumballMachine; public SoldOutState (GumballMachine gumballMachine) { this .gumballMachine = gumballMachine; } public void insertQuarter () { System.out.println("You can't insert a quarter, the machine is sold out" ); } public void ejectQuarter () { System.out.println("You can't eject, you haven't inserted a quarter yet" ); } public void turnCrank () { System.out.println("You turned, but there are no gumballs" ); } public void dispense () { System.out.println("No gumball dispensed" ); } public void refill () { gumballMachine.setState(gumballMachine.getNoQuarterState()); } public String toString () { return "sold out" ; } }
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package headfirst.designpatterns.state.gumballstate;public class GumballMachineTestDrive { public static void main (String[] args) { GumballMachine gumballMachine = new GumballMachine (2 ); System.out.println(gumballMachine); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.refill(5 ); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine); } }
测试结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Mighty Gumball, Inc. Java-enabled Standing Gumball Model #2004 Inventory: 2 gumballs Machine is waiting for quarter You inserted a quarter You turned... A gumball comes rolling out the slot... Mighty Gumball, Inc. Java-enabled Standing Gumball Model #2004 Inventory: 1 gumball Machine is waiting for quarter You inserted a quarter You turned... A gumball comes rolling out the slot... Oops, out of gumballs! You can't insert a quarter, the machine is sold out You turned, but there are no gumballs No gumball dispensed The gumball machine was just refilled; its new count is: 5 You inserted a quarter You turned... A gumball comes rolling out the slot... Mighty Gumball, Inc. Java-enabled Standing Gumball Model #2004 Inventory: 4 gumballs Machine is waiting for quarter
优点 封装了转换规则
枚举可能的状态,在枚举状态之前需要确定状态种类
将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点 必然增加系统类和对象的个数
结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱
对”开闭原则”支持不太好,对可切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码
为所有状态创建单独的类会使系统变得复杂。对于实体状态类来说,有些行为来自于自身,有些行为继承自抽象基类,这一方面在子类和父类之间形成紧耦合,另一方面使代码的可读性变差。但是,使用枚举则非常简单,与通过状态模式来创建标准类型相比,枚举可能是更好的方法。
这里我们同时得到了两种方法的好处: 获得了一个非常简单的标准类型,又能有效地表示当前的状态
策略模式与状态模式 策略模式是定义一个算法家族,把他们封装起来,使得他们之间可以相互替换,
状态模式是将一个个状态封装成一个个类,当内部状态发生改变时,改变他们的行为。
策略模式和状态模式的类图几乎一样,策略模式中,客户端知道具体的策略有哪些,客户端能够通过setStrategy方法来动态的设置具体使用哪个策略,状态模式中,客户端不知道内部状态是怎么变化的,状态模式通过状态转移来组合State对象,最后把行为呈现出来
适用场景 代码中包含大量与对象状态有关的条件语句:
对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为
代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态