一个类对外提供了多个行为,同时该类对象有多种状态,不同状态下对外的
行为的表现不同,我们该如何来设计该类让它对状态可以灵活扩展?
以自动售卖饮料机为例开发一个程序:
public class DrinksSellingMachine { //未支付 final static int NO_PAY = 0; //已支付 final static int PAY = 1; //待取货 final static int SOLD = 2; //已售罄 final static int SOLD_OUT = 4; //当前状态 private int state = SOLD_OUT; //当前库存 private int store; public DrinksSellingMachine(int store) { this.store = store; if(this.store > 0) { this.state = NO_PAY; } } /** 支付 */ public void pay() { switch (this.state) { case NO_PAY : System.out.println("支付成功,请确定购买饮料。"); this.state = PAY; break; case PAY : System.out.println("已支付成功,请确定购买饮料。"); break; case SOLD : System.out.println("待取饮料中,请稍后购买。"); break; case SOLD_OUT : System.out.println("饮料已售罄,不可购买。"); break; } } /** 退款 */ public void refund() { switch (this.state) { case NO_PAY : System.out.println("尚未支付,请不要乱按。"); break; case PAY : System.out.println("退款成功。"); this.state = NO_PAY; break; case SOLD : System.out.println("已购买,请取用。"); break; case SOLD_OUT : System.out.println("饮料已售罄,不可购买。"); break; } } /** 购买 */ public void buy() { switch (this.state) { case NO_PAY : System.out.println("尚未支付,请不要乱按。"); break; case PAY : System.out.println("购买成功,请取用。"); this.state = SOLD; break; case SOLD : System.out.println("已购买,请取用。"); break; case SOLD_OUT : System.out.println("饮料已售罄,不可购买。"); break; } } /** 取货 */ public void getGoods() { switch (this.state) { case NO_PAY : System.out.println("尚未支付,请不要乱按。"); break; case PAY : System.out.println("已购买,请取用。"); case SOLD : System.out.println("正在出货中,请等待三秒。"); this.store--; if (this.store == 0) { this.state = SOLD_OUT; } else { this.state = NO_PAY; } break; case SOLD_OUT : System.out.println("饮料已售罄,不可购买。"); break; } } }
看到上面一大段代码,感觉很懵,如果现在需要扩展状态怎么办,需要对每一种操作下的方法进行修改,这将是一件麻烦的事情
如何能够灵活的扩展状态呢
四种操作是不会变的,但是状态是可以灵活变化的,下面改进代码:
定义一个接口,每一种状态需要实现该接口:
public interface State { /** 支付 */ void pay(); /** 退款 */ void refund(); /** 购买 */ void buy(); /** 取货 */ void getGoods(); }
未支付状态实现类:
public class NoPayState implements State { private NewDrinksSellingMatchine matchine; public NoPayState(NewDrinksSellingMatchine matchine) { this.matchine = matchine; } @Override public void pay() { System.out.println("支付成功,请确定购买饮料。"); this.matchine.state = this.matchine.PAY; } @Override public void refund() { System.out.println("尚未支付,请不要乱按。"); } @Override public void buy() { System.out.println("尚未支付,请不要乱按。"); } @Override public void getGoods() { System.out.println("尚未支付,请不要乱按。"); } }
支付状态实现类:
public class PayState implements State{ private NewDrinksSellingMatchine matchine; public PayState(NewDrinksSellingMatchine matchine) { this.matchine = matchine; } @Override public void pay() { System.out.println("已支付成功,请确定购买饮料。"); } @Override public void refund() { System.out.println("退款成功。"); this.matchine.state = this.matchine.NO_PAY; } @Override public void buy() { System.out.println("已购买,请取用。"); this.matchine.state = this.matchine.SOLD; } @Override public void getGoods() { System.out.println("请先确定购买。"); } }
其他两种状态就不在这展示了,和上面两种差不多
public class NewDrinksSellingMatchine { final State NO_PAY, PAY, SOLD, SOLD_OUT; State state; int store; public NewDrinksSellingMatchine(int store) { NO_PAY = new NoPayState(this); PAY = new PayState(this); SOLD = new SoldState(this); SOLD_OUT = new SoldOutState(this); this.store = store; if(this.store > 0) { this.state = NO_PAY; } } public void pay() { this.state.pay(); } public void refund() { this.state.refund(); } public void buy() { this.state.buy(); } public void getGoods() { this.state.getGoods(); } }
改进之后,如果需要对状态进行扩展,只需要实现State的接口就行了
一个类对外提供了多个行为,同时该类对象有多种状态,不同状态下对外的
行为的表现不同
允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类
对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为
代码中包含大量与对象状态有关的条件语句
优点:
缺点:
类图:
涉及的角色:
对应的代码如下:
State接口:
public interface State { void sampleOperation(); }
ConcreteState类:
public class ConcreteState implements State { @Override public void sampleOperation() { } }
Context类:
public class Context { private State state; public void sampleOperation() { this.state.sampleOperation(); } public void setState(State state) { this.state = state; } }
曾侯乙编钟,1979年在湖北出土,一套共65件,总音域跨5个八度,12个半音齐全,每一只钟都发出不同的音
下面以曾侯乙编钟为例,熟悉一下状态模式:
每一只钟都需要实现的接口:
public interface ClockState { /** 打击钟 */ void blow(); void otherClock(); }
具体每一只钟的实现:
public class ClockConcreteStateC implements ClockState { @Override public void blow() { System.out.println("钟C被打击"); } @Override public void otherClock() { System.out.println("钟A B没有被打击"); } }
public class ClockConcreteStateB implements ClockState { @Override public void blow() { System.out.println("钟B被打击"); } @Override public void otherClock() { System.out.println("钟A C没有被打击"); } }
public class ClockConcreteStateA implements ClockState { @Override public void blow() { System.out.println("钟A被打击"); } @Override public void otherClock() { System.out.println("钟B C没有被打击"); } }
曾候乙编钟,乐师选择一个钟打击它,每一只钟的每个音代表一个状态:
public class ClockContext { private ClockState state; public void blow() { this.state.blow(); this.state.otherClock(); } public void setState(ClockState state) { this.state = state; } }
测试类:
public class Test { public static void main(String[] args) { ClockContext context = new ClockContext(); context.setState(new ClockConcreteStateA()); context.blow(); } }
类图:
注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个
策略模式:侧重的是一个行为的多个算法实现,可互换算法,比如优惠活动满减和打折都是算法,可以选择其中一个来买买买
命令模式:侧重的是为多个行为提供灵活的执行方式,比如上面的奶茶,每个顾客购买奶茶的命令都是一个行为,而不同的奶茶制作方式不一样,则就需要灵活的去制作奶茶
状态模式:应用于状态机的情况
设计原则: