1. 意图(Intent)
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。
2. 类图(Class Diagram)
3. 实现(Implementation)
I 商品存储案例
商品库存中心有个最基本的需求是减库存和补库存,我们看看怎么用状态模式来写。
核心在于,我们的关注点不再是 Context 是该进行哪种操作,而是关注在这个 Context 会有哪些操作。
定义状态接口:[ State ]
public interface State {public void doAction(Context context);}
[ ConcreteState ]
定义减库存的状态:
public class DeductState implements State {public void doAction(Context context) {System.out.println("商品卖出,准备减库存");context.setState(this);//... 执行减库存的具体操作}public String toString() {return "Deduct State";}}
定义补库存状态:
public class RevertState implements State {public void doAction(Context context) {System.out.println("给此商品补库存");context.setState(this);//... 执行加库存的具体操作}public String toString() {return "Revert State";}}
前面用到了 context.setState(this),我们来看看怎么定义 Context 类::[ Context ]
public class Context {private State state;private String name;public Context(String name) {this.name = name;}public void setState(State state) {this.state = state;}public void getState() {return this.state;}}
我们来看下客户端调用,大家就一清二楚了:
public static void main(String[] args) {// 我们需要操作的是 iPhone XContext context = new Context("iPhone X");// 看看怎么进行补库存操作State revertState = new RevertState();revertState.doAction(context);// 同样的,减库存操作也非常简单State deductState = new DeductState();deductState.doAction(context);// 如果需要我们可以获取当前的状态// context.getState().toString();}
读者可能会发现,在上面这个例子中,如果我们不关心当前 context 处于什么状态,那么 Context 就可以不用维护 state 属性了,那样代码会简单很多。
不过,商品库存这个例子毕竟只是个例,我们还有很多实例是需要知道当前 context 处于什么状态的。
II 糖果销售机案例
糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。
[ State ]
public interface State {// 投入 25 分钱void insertQuarter();// 退回 25 分钱void ejectQuarter();// 转动曲柄void turnCrank();// 发放糖果void dispense();}
[ ConcreteState ]
有四分之一状态
public class HasQuarterState implements State {private GumballMachine gumballMachine; // 口香糖机public HasQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("You can't insert another quarter");}@Overridepublic void ejectQuarter() {System.out.println("Quarter returned");gumballMachine.setState(gumballMachine.getNoQuarterState());}@Overridepublic void turnCrank() {System.out.println("You turned...");gumballMachine.setState(gumballMachine.getSoldState());}@Overridepublic void dispense() {System.out.println("No gumball dispensed");}}
无四分之一状态
public class NoQuarterState implements State {GumballMachine gumballMachine;public NoQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("You insert a quarter");gumballMachine.setState(gumballMachine.getHasQuarterState());}@Overridepublic void ejectQuarter() {System.out.println("You haven't insert a quarter");}@Overridepublic void turnCrank() {System.out.println("You turned, but there's no quarter");}@Overridepublic void dispense() {System.out.println("You need to pay first");}}
售罄状态
public class SoldOutState implements State {GumballMachine gumballMachine;public SoldOutState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("You can't insert a quarter, the machine is sold out");}@Overridepublic void ejectQuarter() {System.out.println("You can't eject, you haven't inserted a quarter yet");}@Overridepublic void turnCrank() {System.out.println("You turned, but there are no gumballs");}@Overridepublic void dispense() {System.out.println("No gumball dispensed");}}
售出状态
public class SoldState implements State {GumballMachine gumballMachine;public SoldState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("Please wait, we're already giving you a gumball");}@Overridepublic void ejectQuarter() {System.out.println("Sorry, you already turned the crank");}@Overridepublic void turnCrank() {System.out.println("Turning twice doesn't get you another gumball!");}@Overridepublic void dispense() {gumballMachine.releaseBall();if (gumballMachine.getCount() > 0) {gumballMachine.setState(gumballMachine.getNoQuarterState());} else {System.out.println("Oops, out of gumballs");gumballMachine.setState(gumballMachine.getSoldOutState());}}}
[ Context ]
public class GumballMachine {private State soldOutState;private State noQuarterState;private State hasQuarterState;private State soldState;private State state;private int count = 0; // 标记口香糖个数,由构造函数的 numberGumballs 初始化public GumballMachine(int numberGumballs) {count = numberGumballs;soldOutState = new SoldOutState(this);noQuarterState = new NoQuarterState(this);hasQuarterState = new HasQuarterState(this);soldState = new SoldState(this);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();}public void setState(State state) {this.state = state;}public void releaseBall() {System.out.println("A gumball comes rolling out the slot...");if (count != 0) {count -= 1;}}public State getSoldOutState() {return soldOutState;}public State getNoQuarterState() {return noQuarterState;}public State getHasQuarterState() {return hasQuarterState;}public State getSoldState() {return soldState;}public int getCount() {return count;}}
客户端:
public class Client {public static void main(String[] args) {GumballMachine gumballMachine = new GumballMachine(5);# 1gumballMachine.insertQuarter();gumballMachine.turnCrank();# 2gumballMachine.insertQuarter();gumballMachine.ejectQuarter();gumballMachine.turnCrank();# 3gumballMachine.insertQuarter();gumballMachine.turnCrank();# 4gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.ejectQuarter();# 5gumballMachine.insertQuarter();gumballMachine.insertQuarter();gumballMachine.turnCrank();# 6gumballMachine.insertQuarter();gumballMachine.turnCrank();# 7gumballMachine.insertQuarter();gumballMachine.turnCrank();}}
输出:
#1You insert a quarterYou turned...A gumball comes rolling out the slot...#2You insert a quarterQuarter returnedYou turned, but there's no quarterYou need to pay first#3You insert a quarterYou turned...A gumball comes rolling out the slot...#4You insert a quarterYou turned...A gumball comes rolling out the slot...You haven't insert a quarter#5You insert a quarterYou can't insert another quarterYou turned...A gumball comes rolling out the slot...#6You insert a quarterYou turned...A gumball comes rolling out the slot...Oops, out of gumballs#7You can't insert a quarter, the machine is sold outYou turned, but there are no gumballsNo gumball dispensed
