声明:
本节内容主要来自Head First
好吧,现在带你进入策略模式的地盘
【what】
状态模式是什么?
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
封装基于状态的行为,并将行为委托到当前状态。
模式图:
【How】
聪明的人都会讲故事,源自于Head First 的糖果机故事华丽登场啦
从前有一家糖果公司,发现只要给糖果机装上芯片就更能赚钱,上状态图
聪明的程序员写出了糖果机1.0版,是这个样子:
public class GumballMachine {
final static int SOLD_OUT = 0;
final static int NO_QUARTER = 1;
final static int HAS_QUARTER = 2;
final static int SOLD = 3;
int state = SOLD_OUT;
int count = 0;
public GumballMachine(int count) {
this.count = count;
if (count > 0) {
state = NO_QUARTER;
}
}
public void insertQuarter() {
if (state == HAS_QUARTER) {
System.out.println("You can't insert another quarter");
} else if (state == NO_QUARTER) {
state = HAS_QUARTER;
System.out.println("You inserted a quarter");
} else if (state == SOLD_OUT) {
System.out.println("You can't insert a quarter, the machine is sold out");
} else if (state == SOLD) {
System.out.println("Please wait, we're already giving you a gumball");
}
}
public void ejectQuarter() {
if (state == HAS_QUARTER) {
System.out.println("Quarter returned");
state = NO_QUARTER;
} else if (state == NO_QUARTER) {
System.out.println("You haven't inserted a quarter");
} else if (state == SOLD) {
System.out.println("Sorry, you already turned the crank");
} else if (state == SOLD_OUT) {
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
}
public void turnCrank() {
if (state == SOLD) {
System.out.println("Turning twice doesn't get you another gumball!");
} else if (state == NO_QUARTER) {
System.out.println("You turned but there's no quarter");
} else if (state == SOLD_OUT) {
System.out.println("You turned, but there are no gumballs");
} else if (state == HAS_QUARTER) {
System.out.println("You turned...");
state = SOLD;
dispense();
}
}
public void dispense() {
if (state == SOLD) {
System.out.println("A gumball comes rolling out the slot");
count = count - 1;
if (count == 0) {
System.out.println("Oops, out of gumballs!");
state = SOLD_OUT;
} else {
state = NO_QUARTER;
}
} else if (state == NO_QUARTER) {
System.out.println("You need to pay first");
} else if (state == SOLD_OUT) {
System.out.println("No gumball dispensed");
} else if (state == HAS_QUARTER) {
System.out.println("No gumball dispensed");
}
}
public void refill(int numGumBalls) {
this.count = numGumBalls;
state = NO_QUARTER;
}
public String toString() {
}
}
然而,该来的躲不掉,程序员的噩耗->> 变更需求来啦!
糖果公司CEO决定加入一个买十次赢一次的游戏,10%的机会糖果机将会掉下两颗糖。
事情变得复杂了,看样子每个方法都得增加判断赢家状态的逻辑,这可有的忙了!
重构的时间到了,遵守“封装变化”的原则
①首先,定义一个State接口,在这个接口内,糖果机的每个动作都有一个对应的方法。
②然后为机器的每个状态实现状态类。这些类将负责在对应的状态下进行机器的行为。
③最后,我们要摆脱旧的条件代码,取而代之的方式是,将动作委托到状态类。
定义状态接口和类:
接口:
/*
* 状态接口State
*/
public interface State {
public void insertQuarter(); // 投入25分钱
public void ejectQuarter(); // 拒绝25分钱
public void turnCrank(); // 转动曲柄
public void dispense(); // 发放糖果
}
没有25分
/*
* 没有25分钱状态,实现了State接口
*/
public class NoQuarterState implements State{
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine){
this.gumballMachine=gumballMachine;
}
// 投入25分钱
public void insertQuarter() {
System.out.println("You insert a quarter");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
// 拒绝25分钱
public void ejectQuarter() {
System.out.println("You haven't insert a quarter");
}
// 转动曲柄
public void turnCrank() {
System.out.println("You turned crank,but you there's no quarter");
}
// 发放糖果
public void dispense() {
System.out.println("You need to pay first");
}
}
有25分
/*
* 有25分钱状态,实现了State接口
*/
public class HasQuarterState implements State {
Random randomWinner = new Random(System.currentTimeMillis()); //首先我们增加一个随机数产生器,产生10%赢的机
会
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine){
this.gumballMachine = gumballMachine;
}
// 投入25分钱
public void insertQuarter() {
System.out.println("You can not insert anther quarter");
}
// 拒绝25分钱
public void ejectQuarter() {
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
// 转动曲柄
public void turnCrank() {
System.out.println("You turned...");
int winner = randomWinner.nextInt(10);
System.out.println("winner =" + winner);
if((winner ==0) && (gumballMachine.getCount() > 1)) {
gumballMachine.setState(gumballMachine.getWinnerState());
} else {
gumballMachine.setState(gumballMachine.getSoldState());
}
}
// 发放糖果
public void dispense() {
System.out.println("No gumball dispensed");
}
}
售出糖果状态
/*
* 售出糖果状态,实现了State接口
*/
public class SoldState implements State{
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
// 投入25分钱
public void insertQuarter() {
System.out.println("Please wait, we're already giving you a gumball");
}
// 拒绝25分钱
public void ejectQuarter() {
System.out.println("Sorry,you have already turn crank");
}
// 转动曲柄
public void turnCrank() {
System.out.println("trun twice ,doesn't give you anthor gamball!");
}
// 发放糖果
public void dispense() {
gumballMachine.releaseBall();
if(gumballMachine.getCount()>0){
gumballMachine.setState(gumballMachine.getNoQuarterState());
} else {
System.out.println("Opps,out of gamball!");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
}
糖果售罄状态
/*
* 通过售罄状态,实现了State接口
*/
public class SoldOutState implements State{
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine){
this.gumballMachine=gumballMachine;
}
// 投入25分钱
public void insertQuarter() {
System.out.println("You can't insert a quarter, the machine is sold out");
}
// 拒绝25分钱
public void ejectQuarter() {
// TODO Auto-generated method stub
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
// 转动曲柄
public void turnCrank() {
// TODO Auto-generated method stub
System.out.println("You turned, but there are no gumballs");
}
// 发放糖果
public void dispense() {
// TODO Auto-generated method stub
System.out.println("No gumball dispensed");
}
}
赢家状态
/*
* 赢家状态,实现了State接口
*/
public class WinnerState implements State{
GumballMachine gumballMachine;
public WinnerState(GumballMachine gumballMachine){
this.gumballMachine = gumballMachine;
}
// 投入25分钱
@Override
public void insertQuarter() {
// TODO Auto-generated method stub
System.out.println("Please wait, we're already giving you a gumball");
}
// 拒绝25分钱
@Override
public void ejectQuarter() {
// TODO Auto-generated method stub
System.out.println("Sorry,you have already turn crank");
}
// 转动曲柄
@Override
public void turnCrank() {
// TODO Auto-generated method stub
System.out.println("trun twice ,doesn't give you anthor gamball!");
}
// 发放糖果
@Override
public void dispense() {
// TODO Auto-generated method stub
System.out.println("You're a Winner! You get two gumballs for your quarter");
gumballMachine.releaseBall();
if(gumballMachine.getCount() == 0) {
gumballMachine.setState(gumballMachine.getSoldOutState());
} else {
gumballMachine.releaseBall();
if(gumballMachine.getCount()>0){
gumballMachine.setState(gumballMachine.getNoQuarterState());
} else {
System.out.println("Opps,out of gamball!");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
}
}
重新改造糖果机Context
//糖果机上下文环境类Java源文件 GumballMachine.java
package State;
import State.HasQuarterState;
import State.NoQuarterState;
import State.SoldOutState;
import State.SoldState;
import State.WinnerState;
import State.State;
/*
* 糖果机器上下文环境接口类 GumballMachine
*/
public class GumballMachine {
//状态实例
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State winnerState;
// 实例变量state,初始化为糖果售罄状态
State state = soldOutState;
// 记录机器内装有糖果的数目,开始机器是没有装糖果的
int count=0;
// 构造器取得糖果的初始数目并把它放在一个实例变量count中
public GumballMachine(int numberGumballs) {
// 每种状态都创建一个状态实例
soldOutState=new SoldOutState(this);
noQuarterState=new NoQuarterState(this);
hasQuarterState=new HasQuarterState(this);
soldState=new SoldState(this);
winnerState = new WinnerState(this);
this.count = numberGumballs;
// 若超过0颗糖果,就将状态设置为NoQuarterState
if(numberGumballs > 0) {
state = noQuarterState;
}
}
// 取得机器内的糖果数目
public int getCount() {
return count;
}
// 取得糖果售罄状态
public State getSoldOutState() {
return soldOutState;
}
// 取得没有25分钱状态
public State getNoQuarterState() {
return noQuarterState;
}
// 取得拥有25分钱
public State getHasQuarterState() {
return hasQuarterState;
}
// 取得售出糖果状态
public State getSoldState() {
return soldState;
}
// 取得赢家状态
public State getWinnerState() {
return winnerState;
}
// 投入25分钱
public void insertQuarter(){
state.insertQuarter();
}
// 拒绝25分钱
public void ejectQuarter(){
state.ejectQuarter();
}
// 转动曲柄
public void turnCrank(){
state.turnCrank();
state.dispense();
}
// 设置状态
public void setState(State state){
this.state=state;
}
// 糖果滚出来一个
public void releaseBall(){
System.out.println("A gumball comes rolling out of the solt...");
if(count!=0){
count--;
}
}
}
测试类
//测试糖果机的Java源文件 GumballMachineTestDrive.java
package State;
/*
* 糖果机测试驱动程序:GumballMachineTestDrive.java
*/
public class GumballMachineTestDrive {
/**
* @param args
*/
public static void main(String[] args) {
GumballMachine gumballMachine = new GumballMachine(5);
System.out.println(gumballMachine);
System.out.println("The current gumball number is:" + gumballMachine.getCount());
System.out.println("****************************************");
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
System.out.println("The current gumball number is:" + gumballMachine.getCount());
System.out.println("****************************************");
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
System.out.println("The current gumball number is:" + gumballMachine.getCount());
System.out.println("****************************************");
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
System.out.println("The current gumball number is:" + gumballMachine.getCount());
System.out.println("****************************************");
}
}
【why】
为什么需要状态模式?
【where & when】
应用的场合?时间?
状态模式 和 策略模式 区别:
两个模式的类图是很相似的
以状态模式而言,我们将一群行为封装在状态对象中,context的行为随时可委托到那些状态对象中的一个。随着时间的流逝,当前状态在状态对象集合中游走改变,以反映出context内部的状态,因此,context的行为也跟着改变。但context客户对于状态了解不多,甚至浑然不觉。
而以策略模式而言,客户通常主动指定context所要组合的策略对象是哪一个。策略模式让我们具有弹性,能够在运行时改变策略,但对某个context对象来说,通常只有一个最适合的策略对象。
一般来说,我们把策略模式想成是除了继承之外的一种弹性替代方案。如果你使用继承定义了一个类的行为,你将被这个类困住,甚至想要修改都很难,有了策略模式,你可以通过组合不同的对象来改变行为。
我们把状态模式想成是不用在context中放置很多条件判断的替代方案,通过将行为包装在状态对象中,你可以通过在context内简单地改变状态对象来改变context的行为。
【总结】
糖果机的故事太TM牛了~~
最后,猴子来镇楼~~