在软件系统中,有些对象也像水一样具有多种状态,这些状态在某些情况下能够相互转换,而且对象在不同的状态下也将具有不同的行为。为了更好地对这些具有多种状态的对象进行设计,可以使用一种被称为状态模式的设计模式。
状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化。对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致性地处理。
状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。
在状态模式中引入了抽象状态类和具体状态类,它们是状态模式的核心,其结构如下图所示。
可以看出,在状态模式结构图中包含以下3个角色:
在状态模式中,将对象在不同状态下的行为封装到不同的状态类中。为了让系统具有更好的灵活性和可扩展性,同时对各状态下的共有行为进行封装,需要对状态进行抽象,引入了抽象状态类角色,其典型代码如下:
/**
* @Description: 抽象状态
* @Author: yangyongbing
* @CreateTime: 2023/08/03
* @Version: 1.0
*/
abstract class State {
// 声明抽象业务方法,不同的具体状态类可以有不同的方法实现
public abstract void handle();
}
在抽象状态类的子类即具体状态类中实现了在抽象状态类中声明的业务方法,不同的具体状态类可以提供完全不同的方法实现。在实际使用时,一个状态类中可能包含多个业务方法。如果在具体状态类中某些业务方法的实现完全相同,可以将这些方法移至抽象状态类,实现代码的复用。典型的具体状态类代码如下:
/**
* @Description: 具体状态
* @Author: yangyongbing
* @CreateTime: 2023/08/03 19:45
* @Version: 1.0
*/
public class ConcreteState extends State{
@Override
public void handle() {
// 方法具体实现
}
}
环境类维持一个对抽象状态类的引用。通过setState()方法可以向环境类注入不同的状态对象,再在环境类的业务方法中调用状态对象的方法,典型代码如下:
/**
* @Description: 环境类
* @Author: yangyongbing
* @CreateTime: 2023/08/03 19:46
* @Version: 1.0
*/
public class Context {
// 持有一个对抽象状态对象的引用
private State state;
// 其它属性值,该属性值的变化可能会导致对象状态发生变化
private int value;
// 设置状态对象
public void setState(State state) {
this.state = state;
}
public void request(){
// 其它代码
state.handle(); // 调用状态对象的业务方法
// 其他代码
}
}
环境类实际上是真正拥有状态的对象,这里只是将环境类中与状态有关的代码提取出来封装到专门的状态类中。
在状态模式的使用过程中,一个对象的状态之间还可以进行相互转换。通常有以下两种实现状态转换的方式:
public void changeState(){
// 判断属性值,根据属性值进行状态转换
if(value==0){
this.setState(new ConcreteStateA());
}else if(value==1){
this.setState(new ConcreteStateB());
}
}
public void changeState(Context ctx){
// 判断属性值,根据属性值进行状态转换
if(ctx.getValue()==1){
this.setState(new ConcreteStateB());
}else if(ctx.getValue()==2){
this.setState(new ConcreteStateC());
}
}
在有些情况下,多个环境对象可能需要共享同一个状态。如果希望在系统中实现多个环境对象共享一个或多个状态对象,那么需要将这些状态对象定义为环境类的静态成员对象。
下面通过一个简单实例来说明如何实现共享状态。如果某系统要求两个开关对象要么都处于开的状态,要么都处于关的状态,在使用时它们的状态必须保持一致。开关可以由开转换到关,也可以由关转换到开。
状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中。通过设置不同的状态对象可以让环境对象拥有不同的行为,而状态转换的细节对于客户端而言是透明的,方便了客户端的使用。在实际开发中,状态模式具有较高的使用频率,在工作流、游戏等软件中状态模式都得到了广泛应用,例如公文状态的转换、游戏中角色的升级等。