【设计模式】一文速通状态模式

79 阅读4分钟

1. 引入

假设此时电灯有两种状态:开启(On)和 关闭(Off)。

规定当电灯处于不同的状态下,按下开关会触发不同的行为:

  • 电灯处于On状态时,按下开关,电灯会处于Off状态;
  • 电灯处于Off状态时,按下开关 ,电灯会处于On状态。

如何实现上述需求呢?有以下两种方案。

1.1 方案一:if-else实现

1.1.1 实现代码

class Light {

    // 电灯状态
    private boolean isOn;

    public Light() {
     // 初始状态为关闭
        this.isOn = false; 
    }

   // 不同状态按下开关逻辑
    public void toggleSwitch() {
        if (isOn) {
        // 开启状态按下开关,状态变为关闭
            System.out.println("Light is turned OFF");
            isOn = false;
        } else {
        // 关闭状态按下开关,状态变为开启
            System.out.println("Light is turned ON");
            isOn = true;
        }
    }
}

1.1.2 上述方案存在问题

如果电灯添加新的状态,则需要修改toggleSwitch()的逻辑,这种方式的两个弊端 :

  • 如果状态很多,条件语句会越来越多,会导致方法复杂,使代码难以理解和维护;

  • 每次添加状态都需要修改方法,可能会影响现有的状态切换逻辑,容易引入错误 。

【例如】 假设我们为电灯添加一个新状态,称为调光(Dim)状态,该种状态下 ,电灯的亮度比较低,但仍然是开启状态。

class Light {
    private boolean isOn;
    // 新增调光Dim状态
    private boolean isDim;

    public Light() {
        this.isOn = false; // 初始状态为关
        this.isDim = false; // 初始不是调光状态
    }

    public void toggleSwitch() {

       // 增加状态后修改状态转换逻辑
        if (isOn) {
            if (isDim) {
                System.out.println("Light is turned OFF");
                isOn = false;
                isDim = false;
            } else {
                System.out.println("Light is turned Dim");
                isDim = true;
            }
        } else {
            System.out.println("Light is turned ON");
            isOn = true;
        }
    }
}

总结

可以看到,上述例子新增了一个状态,方法的复杂程度就上升了一个级别,如果新增的不是一个状态,而是五个,十个甚至百个呢,这种实现方案明显不是一个好的选择。因此,这种方案只适合状态比较少且切换逻辑不是很复杂的情形。

1.2 方案二:采用状态模式实现

1.2.1 实现代码

  • 定义一个状态接口(或抽象类)
// 状态接口
interface State {
    void toggleSwitch(Light light);
}
  • 为每个具体状态创建实现类
// 具体状态类:灯关闭状态
class OffState implements State {
    @Override
    public void toggleSwitch(Light light) {
        System.out.println("Light is turned ON");
        light.setState(new OnState());
    }
}

// 具体状态类:灯打开状态
class OnState implements State {
    @Override
    public void toggleSwitch(Light light) {
        System.out.println("Light is turned Dim");
        light.setState(new DimState());
    }
}

// 具体状态类:灯调光状态
class DimState implements State {
    @Override
    public void toggleSwitch(Light light) {
        System.out.println("Light is turned OFF");
        light.setState(new OffState());
    }
}
  • 电灯将根据当前状态委托相关操作给状态对象
// 环境类,电灯
class Light {
    private State state;

    public Light() {
        // 初始状态为关
        this.state = new OffState();
    }

    public void setState(State state) {
        this.state = state;
    }

    public void toggleSwitch() {
        state.toggleSwitch(this);
    }
}

image.png

总结

可以看到,采用状态模式添加新状态代码复杂度大大降低,只需要创建新的实现类并实现 State接口,然后在合适的位置进行状态切换即可。这种设计避免了复杂的条件语句,使代码更加清晰、可维护和可扩展。

2. 状态模式

2.1 定义

状态模式是一种行为设计模式,能在一个对象的内部状态变化时改变其行为,使其看上去就像改变了自身所属的类一样。

2.2 角色及结构

2.2.1 三大角色

  • State抽象状态角色):一个接口或抽象类,负责定义对象的状态,封装环境据角色,从而实现状态切换;
  • ConcreteState具体状态角色):实现状态接口或继承抽象状态类,具体定义每个状态下的行为,主要包括:当前这个状态要做的事,以及这个状态如何过渡到其他状态。
  • Context上下文 / 环境角色):定义客户端需要的接口,负责具体状态的切换。

2.2.2 结构

image.png

2.3 适用场景

  • 行为随状态改变而改变的场景;

  • 条件、 分支判断语句的替代者。

2.4 优缺点

2.4.1 优点

  • 遵循单一职责原则: 将与特定状态相关的代码放在单独的类中;

  • 遵循开闭原则:无需修改已有状态类和上下文就能引入新状态;

  • 通过消除臃肿的状态机条件语句简化上下文代码。

2.4.2 缺点

  • 状态的子类可能会太多, 也就是类膨胀。

2.5 注意事项

适用于当某个对象在它的状态发生改变时, 它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。

参考资料