首先了解下状态模式:
策略模式围绕可以互换的算法来创建成功业务的,然而状态走的是更崇高的路,它通过改变对象内部的状态来帮助对象控制自己的行为。状态模式对于需要针对不同状态对某一业务进行不同处理的场景特别有用,尤其在扩展的时候,不用去修改逻辑错综复杂的代码。
我们不要针对业务流程去编程,而是要针对状态来编程
1.定义一个State接口。在这个接口内,糖果机的每个动作都有一个对应的方法。
2.然后为机器中的每个状态实现状态类。这些状态类将负责在对应的状态下进行机器的行为。
3.最后我们将动作委托到状态类(这个还是不太明白)
我们将一个状态的所有行为放在一个类中,这样一来我们的行为就局部化了,并使得事情更容易改变和理解(更容易改变和理解是关键)
第一个版本的糖果机实现如下:
1.糖果机
package com.dlo.designpatterns.state;
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 ;
// 糖果机状态,一共4个状态
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
// 一个成员对象用来保存当前状态
State state = soldOutState;
// 一个成员变量保存机器状态数
int count = 0;
// 构造函数初始化各个状态成员对象及总状态数
public GumballMachine(int numberGumballs) {
super();
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 setState(State state) {
this.state = state;
}
void releaseBall() {
System.out.println("A gumball comes rolling out theslot...");
if (count != 0) {
count = count - 1;
}
}
public State getHasQuarterState() {
// TODOAuto-generated method stub
return null;
}
public State getSoldOutState() {
return soldOutState;
}
public void setSoldOutState(State soldOutState) {
this.soldOutState = soldOutState;
}
public State getNoQuarterState() {
return noQuarterState;
}
public void setNoQuarterState(State noQuarterState) {
this.noQuarterState = noQuarterState;
}
public State getSoldState() {
return soldState;
}
public void setSoldState(State soldState) {
this.soldState = soldState;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public State getState() {
return state;
}
public void setHasQuarterState(State hasQuarterState) {
this.hasQuarterState = hasQuarterState;
}
}
这个糖果机包含了几个状态对象成员和一个标识状态数的成员变量,自动生成了getset函数,当然我们下一步就是要创建这些状态类了。不过我们创建的状态类都继承了一个State接口,这个接口定义了每个状态都有的一些行为,所以我们先创建接口。
2.
package com.dlo.designpatterns.state;
public interface State {
public void dispense();
public void turnCrank();
public void ejectQuarter();
public void insertQuarter();
}
3.然后创建状态类并实现相应的接口方法
package com.dlo.designpatterns.state;
public class HasQuarterState implements State {
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
// TODOAuto-generated constructor stub
this.gumballMachine = gumballMachine;
}
@Override
public void dispense() {
// TODOAuto-generated method stub
System.out.println("No gumball dispensed");
}
@Override
public void turnCrank() {
// TODOAuto-generated method stub
System.out.println("You turned...");
gumballMachine.setState(gumballMachine.getSoldState());
}
@Override
public void ejectQuarter() {
// TODOAuto-generated method stub
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
@Override
public void insertQuarter() {
// TODOAuto-generated method stub
System.out.println("You can't insert another quarter");
}
}
像这样,在糖果机中调用行为的时候,比如调用
public void turnCrank(){
的时候,就会调用相应状态类中的turnCrank和dispense方法。这样的话,如果有新增的状态加进来,创建新的状态类就是,易于扩展。
我们要做的事情就是实现适合我们所在的这个状态的行为。在某些情况下,这个行为会让糖果机的状态改变。
(这里我们把状态不是用一个变量来标识,而是状态对象)。
状态模式的好处是
1.将每个状态的行为局部化到它自己的类中了。
2.将容易产生问题的if语句删除,方便了以后的维护。
3.让每一个状态 "对修改关闭",让糖果机"对扩展开放",因为可以加入新的状态类。
4.创建一个新的代码基和类结构,结构要比针对行为编程那样用条件判断来处理不同状态要清晰。
这个例子中的gumballMachine就是一个Context,上下文(这点非常重要),context的行为随时可以委托到那些状态对象中的一个来处理。
通过将行为包装进状态对象中,我们可以通过在context内简单地改变状态对象来改变context的行为。
这里我们一个状态的下一个状态是确定的,所以放在了context中来处理,如果下一个状态是不确定的,我们就可以把这个状态转换放到状态类中来处理。
但是放在状态类中的话,各个状态类就会产生依赖(在我们这个例子里,还使用了get,set函数来把依赖减小,而不是直接硬编码具体状态。)