背景:
糖果机有四种状态——有钱、没钱、售出糖果、糖果售完,客户有四种行为:投钱、退钱、转动曲柄、发放糖果。现在需要设计一个能够尽量有弹性而且好维护的控制器。
方案一:创建四个方法对应客户四种行为,并用if/esle语句判断糖果机的状态,以执行不同回应。
不可行:一旦多增加一个状态(如玩游戏),则需要修改全部方法,不符合“对修改关闭,对扩展打开”原则;不符合面向对象原则。
设计:
- 定义一个state接口,在接口内,糖果机的每个动作都有一个对应的方法;
- 然后为机器中的每个状态实现状态类,这些类将负责在对应的状态下进行机器的行为。
- 摆脱条件代码,将动作委托到状态类。
实现:
// 定义状态类接口
public interface State {
insertQuarter();
ejectQuarter();
tumCrank();
dispense();
}
有四种状态:有钱、没钱、售出、售空 这里只列举一个状态
// 没钱的状态
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 insert a quarter");
}
// 装动曲柄
public void turnCrank() {
System.out.println("You turnes, but there's no quarter");
}
// 发糖果
public void dispense() {
System.out.println("You need to pay first");
}
}
// 糖果机
public class GumballMachine {
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State state = soldOutState; // 默认是没有糖果的
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;
}
}
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--;
}
}
}
// 主方法
public class GumballMachineDrive {
public static void main(String[] args){
GumballMachine gumballMachine = new GumballMachine(5);
gumballMachine.insertQuarter();
gumballMachine.turnGrank();
}
}
总结:
状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
这个模式将状态封装成独立的类,并将动作委托到代表当前状态的对象,行为会随着内部状态的改变而改变。
策略模式和状态模式比较:
- 状态模式:我们将一群行为封装在状态对象中,context(如本文的糖果机)的行为随时可以委托到哪些状态对象中的一个。随着时间的流逝,当前状态在状态对象集合中游走改变,以反应出context内部的状态,因此,context的行为也会跟着改变。但是context的客户对状态对象了解不多,甚至浑然不觉;
- 策略模式:客户通常主动指定Context所要组合的策略对象是哪一个。现在固然策略模式让我们更具有弹性,能够在运行时改变策略,但对于某个context对象来说,通常都只有一个最适当的策略对象。比如鸭子被设置为不同的飞行方式。
状态模式用类代表状态,可以被多个Context实例共享。