2、15状态模式
定义
状态模式,当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简单,那就没必要用”状态模式”了。
状态切换示意图
通用类图
1)State抽象状态角色
定义了一个所有具体状态的共同接口/抽象类(负责对象状态定义),任何状态都实现这个相同的接口或抽象类,并且封装环境角色,这样一来,状态之间可以互相替换。
2)ConcreteState具体状态角色
处理来自Context的请求。每一个ConcreteState都提供了它自己对于请求的实现。所以,当Context改变状态时行为也跟着改变。
每个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态。
3)Context上下文环境角色
定义客户端需要的接口,可以拥有一些内部状态,并且负责具体状态的切换。
不管是在什么时候,只要有人调用Context的request()(方法名可能不一样,只是一个代理名称,案例中是handle1和handle2)方法,它就会被委托到状态(调用state.handle)来处理。
通用案例
/**
* 具体环境角色
* TODO 这里已经隐藏了状态的变化过程,它的切换引起了行为的变化。
* TODO 对外来说,我们只看到行为的发生改变,而不用知道是状态变化引起的。
*/
public class Client1 {
public static void main(String[] args) {
//定义环境角色
Context context = new Context();
//初始化状态
context.setCurrentState(new ConcreteState1());
//行为执行
context.handle1();
context.handle2();
}
}
abstract class State{
//定义一个环境角色,提供子类访问
protected Context context;
//设置环境角色
public void setContext(Context context) {
this.context = context;
}
//行为1
abstract void handle1();
//行为2
abstract void handle2();
}
/**
* 环境角色1
*/
class ConcreteState1 extends State{
@Override
void handle1() {
System.out.println("----------ConcreteState1--handle1----------");
}
@Override
void handle2() {
System.out.println("----------ConcreteState1--handle2----------");
}
}
/**
* 环境角色2
*/
class ConcreteState2 extends State{
@Override
void handle1() {
System.out.println("----------ConcreteState2--handle1----------");
}
@Override
void handle2() {
System.out.println("----------ConcreteState2--handle2----------");
}
}
class Context{
//定义状态
public final static State STATE1 = new ConcreteState1();
public final static State STATE2 = new ConcreteState2();
//当前状态
private State currentState;
//获得当前状态
public State getCurrentState() {
return currentState;
}
//设置当前状态
public void setCurrentState(State currentState) {
this.currentState = currentState;
//切换状态
this.currentState.setContext(this);
}
//行为委托1
void handle1() {
this.currentState.handle1();
}
//行为委托2
void handle2() {
this.currentState.handle2();
}
}
输出结果:
----------ConcreteState1--handle1----------
----------ConcreteState1--handle2----------
业务案例(电梯)类图
业务案例(电梯)代码
/**
* 场景类
*/
public class Client2 {
public static void main(String[] args) {
Context context = new Context();
context.setLiftState(new ClosingState());
context.open();
context.close();
context.run();
context.stop();
}
}
/**
* 抽象电梯状态
*/
abstract class LiftState{
//定义一个环境角色,也就是封装状态的变化引起的功能变化
protected Context context;
public void setContext(Context context) {
System.out.println(context);
this.context = context;
}
//首先电梯门开启动作
abstract void open();
//电梯门有开启,那么当然就有关闭了
abstract void close();
//电梯要能上能下,运行起来
abstract void run();
//电梯还要能停下来
abstract void stop();
}
/**
* 上下文类
*/
class Context{
public final static OpenningState OPENNING_STATE = new OpenningState();
public final static ClosingState CLOSING_STATE = new ClosingState();
public final static RunningState RUNNING_STATE = new RunningState();
public final static StoppingState STOPPING_STATE = 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();
}
}
/**
* 敞门状态
*/
class OpenningState extends LiftState{
/**
* 电梯门开启动作
*/
@Override
void open() {
System.out.println("敞门状态:电梯门开启");
}
/**
* 电梯门关闭
*/
@Override
void close() {
//状态修改
super.context.setLiftState(Context.CLOSING_STATE);
//动作委托为ClosingState来执行
super.context.getLiftState().close();
}
/**
* 电梯运行起来
*/
@Override
void run() {
}
/**
* 电梯停下来
*/
@Override
void stop() {
}
}
/**
* 关闭状态
*/
class ClosingState extends LiftState{
/**
* 电梯门开启动作
*/
@Override
void open() {
super.context.setLiftState(Context.OPENNING_STATE);
super.context.getLiftState().open();
}
/**
* 电梯门关闭
*/
@Override
void close() {
System.out.println("关闭状态:电梯门关闭");
}
/**
* 电梯运行起来
*/
@Override
void run() {
super.context.setLiftState(Context.RUNNING_STATE);
super.context.getLiftState().run();
}
/**
* 电梯停下来
*/
@Override
void stop() {
super.context.setLiftState(Context.STOPPING_STATE);
super.context.getLiftState().stop();
}
}
/**
* 运行状态
*/
class RunningState extends LiftState{
/**
* 电梯门开启动作
*/
@Override
void open() {
}
/**
* 电梯门关闭
*/
@Override
void close() {
}
/**
* 电梯运行起来
*/
@Override
void run() {
System.out.println("运行状态:电梯运行");
}
/**
* 电梯停下来
*/
@Override
void stop() {
super.context.setLiftState(Context.STOPPING_STATE);
super.context.getLiftState().stop();
}
}
/**
* 停止状态
*/
class StoppingState extends LiftState{
/**
* 电梯门开启动作
*/
@Override
void open() {
super.context.setLiftState(Context.OPENNING_STATE);
super.context.getLiftState().open();
}
/**
* 电梯门关闭
*/
@Override
void close() {
}
/**
* 电梯运行起来
*/
@Override
void run() {
super.context.setLiftState(Context.RUNNING_STATE);
super.context.getLiftState().run();
}
/**
* 电梯停下来
*/
@Override
void stop() {
System.out.println("停止状态:电梯停下来");
}
}
输出结果:
com.mbox.zhuangtai.example2.Context@7b3300e5
com.mbox.zhuangtai.example2.Context@7b3300e5
敞门状态:电梯门开启
com.mbox.zhuangtai.example2.Context@7b3300e5
关闭状态:电梯门关闭
com.mbox.zhuangtai.example2.Context@7b3300e5
运行状态:电梯运行
com.mbox.zhuangtai.example2.Context@7b3300e5
停止状态:电梯停下来
优点
状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。
将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。
目的是为了消除庞大的条件分支语句,大的分支判断会使得它们难以修改和扩展,任何改动和变化都是致命的。状态模式通过把各种状态转移逻辑分不到State的子类之间,来减少相互间的依赖。
什么时候应该考虑使用状态模式呢?当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。
1)结构清晰
避免了过多的switch...case或者if...else语句的使用,避免了程序的复杂性,提供系统的可维护性。
2)遵循设计原则
很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就要增加子类,你要修改状态,你只修改一个子类就可以了。
3)封装性非常好
这也是状态模式的基本要求,状态变换放置到类的内部来实现,外部的调用不知道类内部如何实现状态和行为的变换。
缺点
如果完全使用状态模式就会有太多的子类,也就是类膨胀,不好管理,需要自己在项目中衡量,解决方案有很多,比如可以通过在数据库中建立状态表来解决。
使用场景
1)行为随状态改变而改变的场景
这是状态模式的根本出发点,例如权限设计、工作流开发。
2)条件、分支判断语句的替代者
通过状态模式很好地避免了这一问题,它通过扩展子类实现了条件的判断处理。
注意事项
状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。
要点
状态模式允许一个对象基于内部状态而拥有不同的行为。
和程序状态机(PSM)不同,状态模式用类代表状态。
Context会将行为委托给当前状态对象。
通过将每个状态封装进一个类,我们把以后需要做的任何改变局部化了。
状态模式和策略模式有相同的类图,但是它们的意图不同。
策略模式通常会用行为或算法来配置Context类。
状态模式允许Context随着状态的改变而改变行为。
状态转换可以由State类或Context类控制。
使用状态模式通常会导致设计中类的数目大量增加。
状态类可以被多个Context实例共享。
标题:设计模式(十五)行为模式(对象行为型模式)状态模式-STATE
作者:yazong
地址:https://blog.llyweb.com/articles/2020/07/28/1595948193561.html