欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

状态模式在 Java 中的应用

最编程 2024-04-16 17:48:40
...

这是我参与更文挑战的第7天,活动详情查看: 更文挑战

业务场景

学过设计模式的兄弟们一定经常在写代码的时候思索, 倒底何时才能使用我的洪荒之力呢?

笔者最近恰好就遇到了最合适不过的需求, 简单介绍下:

  • 需要一直对设备进行监控
  • 可以对设备发送向上移动/向下移动/停止移动指令
  • 设备离线时, 需要重连并进行初始化
  • 设备初始化过程中, 需要获取设备情况
  • 设备正常运行时, 需要获取设备坐标

方案

按照上面的需求, 我们很容易写出如下结构的代码:

if (device.getStatus() == Device.Status.OFFLINE) {
    // 业务代码
} else if (device.getStatus() == Device.Status.INIT) {
    // 业务代码
}
...

乍一看逻辑非常严谨, 但实际情况是, 在向上移动的方法中, 会出现这一堆if, 同时它也会出现在向下移动的方法中, 停止移动的方法中

此时, 我们应该使用状态模式

当一堆相同情况的if语句出现在两个或多个不同的方法中时, 我们就该考虑用状态(State)去替换它

这里多说一句, 状态模式和策略模式的区别是, 状态模式下用户无法感知和参与对象的状态变化, 而策略模式是可以的

编写状态类

首先, 创建一个基础状态类, BaseState.java:

public abstract class BaseState {

    Device device;
    
    protected BaseState(Device device) {
        this.device = device;
    }
    
    /**
   * 心跳链接
   */
   public abstract void heartBeat();

}

继续创建离线状态, 继承自基础状态, OffLineState.java:

public class OffLineState extends BaseState {

    public OffLineState(Device device) {
        super(device);
    }
    
    @Override
    public void heartBeat() {
        homing();
    }
    
    public void homing() {
        try {
            device.homing();
            device.setState(new InitState(device));
        } catch (IOException e) {
            device.setState(new OffLineState(device));
        }
    }
    
}

可以看到, 状态的变化是在内部, 用户无法感知

接下来创建初始化类, InitState.java:

public class InitState extends BaseState {

    public InitState(Device device) {
        super(device);
    }

    @Override
    public void heartBeat() {
        try {
            if (device.isMove()) {
                return;
            }
            device.setZeroPosition(device.currentPosition());
            device.setState(new OnLineState(device));
        } catch (IOException e) {
            device.setState(new OffLineState(device));
        }
    }
    
}

替换方法

接下来, 我们只需要在具体的业务逻辑中, 将代码device.heartBeat()替换为device.getState().heartBeat()

这里不再赘述, 简单展示下笔者的业务相关代码:

public class Connector {

    private static final int INTERVAL = 2000;

    @Async
    public void connect(Device device) {
        try {
            while(true) {
                device.getState().heartBeat();
                Thread.sleep(INTERVAL);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

}

总结

最后总结下状态模式的特点:

  1. 状态类拥有相同的public方法
  2. 状态类拥有一个属性指向使用此状态的实体类
  3. 状态类在内部自动切换状态, 用户无法感知
  4. 用于替换大块重复出现的if语句