简介
当我们需用要程序去描述某个物体的某些事情
的时候,并且这些事情
会随着这个物体的状态
有不同的处理机制。
例如,电梯在运行的时候不可以开门,在停止的时候可以开门;视频在停止的时候只能开启,在播放的时候可以暂停、停止、快进。从这两个例子可以看出,面对物体的当前状态不同
,那么他执行的方法也会产生不同的变化
。
传统方法
我们先来从普通的角度来编写代码;我们可以根据对象当前状态进行判断
从而完成我们希望他去执行的方法:
public class Radio{
String name;
int state;
public void setState(int b){
this.state = b;
}
public void smile() {
if(this.state == 1){
}else if(this.state == 2){
}else if(this.state == 3){
}else{
}
}
public void cry() {
switch(this.state){
case 1:
// todo
break;
case 2:
// todo
break;
default:
// todo
break;
}
}
public void say() {
//todo
}
}
我们可以看到,只是要根据不同状态来决定执行方法,却产生了那么多的if…else语句和switch语句,造成了很多的代码冗余,这个时候我们就可以运用状态模式。
引入状态模式
引入状态模式,我们将每种状态都分离
出来,并且使用一个context
容器记录当前状态。例如,暂停就单独暂停一个类,播放就单独播放一个类。
我们先看抽象父类方法,将对应的方法全部抽象出来,给子类去完成:
public abstract class AbsStatus {
// 容器用于记录状态
protected Context context;
// 用于改变当前状态的方法,比较重要
public void setContext(Context context){
this.context = context;
}
// 暂停
public abstract void close();
// 停止
public abstract void stop();
// 开启播放
public abstract void open();
// 加速
public abstract void speed();
}
停止子类:
public class StopStatus extends AbsStatus {
// 停止状态下无法暂停了
@Override
public void close() {
System.out.println("停止无法关闭");
}
// 本来就停止了不处理
@Override
public void stop() {
System.out.println("本来就停止");
}
// 停止状态下可以打开
@Override
public void open() {
System.out.println("切到打开状态");
// 这两句是核心代码,因为你已经切换到了开打状态,你要通知容器去改变当前状态
super.context.setAbsStatus(Context.OPEN_STATUS);
super.context.getAbsStatus();
}
// 停止状态无法加速
@Override
public void speed() {
System.out.println("无法加速");
}
}
状态容器类:
public class Context {
// 提前定义好各种状态类,后面改状态可直接用
public static final OpenStatus OPEN_STATUS = new OpenStatus();
public static final CloseStatus CLOSE_STATUS = new CloseStatus();
public static final SpeedStatus SPEED_STATUS = new SpeedStatus();
public static final StopStatus STOP_STATUS = new StopStatus();
// 聚合一个抽象状态父类
private AbsStatus absStatus;
// 设置当前的状态
public void setAbsStatus(AbsStatus absStatus){
this.absStatus = absStatus;
// 一定要把当前状态容器传递抽象父类中
this.absStatus.setContext(this);
}
public AbsStatus getAbsStatus() {
return absStatus;
}
public void close() {
this.absStatus.close();
}
public void stop() {
this.absStatus.stop();
}
public void open() {
this.absStatus.open();
}
public void speed() {
this.absStatus.speed();
}
}
同理其他三个状态类也是,下面直接先贴出来了:
播放类
public class OpenStatus extends AbsStatus {
@Override
public void close() {
System.out.println("切到关闭状态");
super.context.setAbsStatus(Context.CLOSE_STATUS);
super.context.getAbsStatus();
}
@Override
public void stop() {
System.out.println("切到关闭状态");
super.context.setAbsStatus(Context.STOP_STATUS);
super.context.getAbsStatus();
}
@Override
public void open() {
System.out.println("本来就打开了");
}
@Override
public void speed() {
System.out.println("加速加速");
super.context.setAbsStatus(Context.SPEED_STATUS);
super.context.getAbsStatus();
}
}
暂停类:
public class CloseStatus extends AbsStatus {
@Override
public void close() {
System.out.println("关闭就关闭呗");
}
@Override
public void stop() {
System.out.println("切到暂停");
super.context.setAbsStatus(Context.STOP_STATUS);
super.context.getAbsStatus();
}
@Override
public void open() {
System.out.println("切到打开");
super.context.setAbsStatus(Context.OPEN_STATUS);
super.context.getAbsStatus();
}
@Override
public void speed() {
System.out.println("切到加速");
super.context.setAbsStatus(Context.SPEED_STATUS);
super.context.getAbsStatus();
}
}
加速类:
public class SpeedStatus extends AbsStatus {
@Override
public void close() {
System.out.println("切到关闭状态");
super.context.setAbsStatus(Context.CLOSE_STATUS);
super.context.getAbsStatus();
}
@Override
public void stop() {
System.out.println("停止");
super.context.setAbsStatus(Context.STOP_STATUS);
super.context.getAbsStatus();
}
@Override
public void open() {
System.out.println("本来就处于打开");
}
@Override
public void speed() {
System.out.println("本来就加速");
}
}
正常状态切换结果演示:
如果拿掉了状态切换:
注意事项
- 这里我们需要注意,我们
将状态(播放,暂停,停止,加速)装配到了context容器
;由context去执行对应的行为
。 - 在执行每个行为的时候,都要
改变当前context的状态
;例如你将收音机从停止状态变到了打开状态,那么需要在停止类调用打开方法的那个方法中改变context的状态,使得当前状态处于打开状态
。 - context类中聚合抽象状态父类,是为了面对抽象编程;调用方在调用的时候在具体调用其子类。
- 抽象状态父类中聚合context容器是
为了让状态跟随行为
,当发生切换状态的动作时(例如从停止到播放状态),也直接将状态进行了切换。
UML类图
- 子类状态继承抽象父类
- 抽象父类状态和Context容器直接互相聚合
小结
这个模式在生活中很常见,但是却在编程中很少这么写(至少我是这样),不像代理模式天天见。所以了解这个模式需要花点时间揣摩。