持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情
定义
当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类
我们来看类图
首先一个状态模式需要三个角色
- 上下文环境类
- 抽象状态类
- 具体状态类
上下文环境类主要是来设置环境,然后去决定调用那个具体状态类
抽象状态类主要是将这些状态所需要的功能方法聚合在一起,供具体状态类去实现
具体实现类是根据环境的状态真正处理的类
举个例子实现一下
我们每天应该都会坐电梯
而电梯也是有很多的状态,并且不同的状态所能做的动作也是不一样的。
需求分析
电梯的状态所能做的动作(粗略的)
- 敞开门的状态:开门的动作;可以关门
- 门关闭的状态:关门的动作;可以开门,可以运行,可以停止
- 电梯运行的状态:运行的动作;只能停止
- 电梯停止的状态:停止的动作;可以开门,可以运行
代码实现一下
首先定义一个状态抽象类
根据上面的分析,我们知道一个电梯有哪些动作,这些动作就是类的方法
我们也要引入电梯环境类,来传递电梯的状态,再执行对象状态的动作
/**
* 电梯抽象状态类,定义电梯的功能方法
*/
abstract class LiftState{
/**
* 引入电梯环境类
*/
protected LiftContext context;
public void setContext(LiftContext context) {
this.context = context;
}
//首先电梯门开启动作
public void open(){
throw new UnsupportedOperationException(context.getLiftState().getClass() + "不支持电梯开门的动作");
}
//电梯门关闭动作
public void close() {
throw new UnsupportedOperationException(context.getLiftState().getClass() + "不支持电梯关门的动作");
}
//电梯要能上能下,运行起来
public void run() {
throw new UnsupportedOperationException(context.getLiftState().getClass() + "不支持电梯上下楼的动作");
}
//电梯还要能停下来
public void stop() {
throw new UnsupportedOperationException(context.getLiftState().getClass() + "不支持电梯停止的动作");
}
}
所有方法都给了空实现,为了让具体实现类只需要重写自己需要重写的方法,不用再去实现与自己职责无关的方法
具体的状态类
根据上面的分析,也能得知,电梯的各个状态,每个状态也就对应着每一个具体的状态实现类。每次调用与非这个状态直接操作的方法都需要改变上下文环境,再用对象的状态对象调用对应的方法
/**
* 敞开门状态
*/
class OpenningState extends LiftState{
@Override
public void open(){
System.out.println("电梯开门");
}
@Override
public void close() {
super.context.setLiftState(LiftContext.closeingState);
super.context.close();
}
}
/**
* 关门状态
*/
class ClosingState extends LiftState{
@Override
public void open(){
super.context.setLiftState(LiftContext.openningState);
super.context.open();
}
@Override
public void close() {
System.out.println("电梯关门");
}
@Override
public void run() {
super.context.setLiftState(LiftContext.runningState);
super.context.run();
}
@Override
public void stop() {
super.context.setLiftState(LiftContext.stoppingState);
super.context.stop();
}
}
/**
* 运行状态
*/
class RunningState extends LiftState{
@Override
public void run() {
System.out.println("电梯在运行中");
}
@Override
public void stop() {
super.context.setLiftState(LiftContext.stoppingState);
super.context.stop();
}
}
/**
* 停止状态
*/
class StoppingState extends LiftState{
@Override
public void open() {
super.context.setLiftState(LiftContext.openningState);
super.context.open();
}
@Override
public void run() {
super.context.setLiftState(LiftContext.runningState);
super.context.run();
}
@Override
public void stop() {
System.out.println("电梯停住");
}
}
最后就是上下文的环境类了
/**
* 电梯环境类
*/
class LiftContext{
//定义出所有的电梯状态
public final static OpenningState openningState = new OpenningState();
public final static ClosingState closeingState = new ClosingState();
public final static RunningState runningState = new RunningState();
public final static StoppingState stoppingState = new StoppingState();
//定义一个当前电梯状态
private LiftState liftState;
public LiftState getLiftState() {
return liftState;
}
public void setLiftState(LiftState liftState) {
this.liftState = liftState;
//把当前的环境通知到各个实现类中
this.liftState.setContext(this);
}
public void open(){
this.liftState.open();
}
public void close(){
this.liftState.close();
}
public void run(){
this.liftState.run();
}
public void stop(){
this.liftState.stop();
}
}
最后
其实这个电梯的案例实现还是可以优化的,本篇具体定义了好几个方法完全是因为这样更好的理解状态模式。
这些开门,关门,运行,停止的方法完全可以统一定义一个处理方法。每一个状态只有一个处理方法,然后根据环境状态的不同去选择相应的具体状态类去处理。
大家可以动动小手去实现一下。
参考文献、资料
《设计模式之禅》