责任链设计模式,简单来说就是使多个对象都有机会处理同一个请求,侧重于代码的可扩展性。 具体表现在对请求的处理进行解耦,责任链上的每一个对象都只处理自己的逻辑,体现了单一职责原则,同时也提高了代码的可扩展性,可以在责任链上新增新的处理对象。 责任链设计模式一般的使用场景有两种,一是责任链上的对象都要按顺序处理某个请求;二是责任链上的任一对象处理了请求,其它对象就不再处理。
1、背景:
公司主营港口无人驾驶,我负责云端,对接港口的自动化设备是工作内容的一部分。无人车在接收到作业指令后要去码头面(船舶停靠处)与岸桥(岸边桥式起重机,负责吊起集装箱)进行自动化交互,完成作业。岸桥自动化设备会周期性(一秒一次)通过MQTT协议向云端发送它的状态与数据,其中主要的数据有岸桥大车位置(岸桥离码头面原点的距离),引导无人车对位的步进距离(无人车要停在一个合适的位置岸桥才能正常作业,所以需要给一个引导值让无人车前进或后退进行对位)、抓箱的吊具尺寸(因为集装箱的尺寸有20尺、40尺、45尺,吊具就类似于抓娃娃机的那个爪子,所以吊具要根据集装箱尺寸进行尺寸变化才能抓住)以及一些状态数据。
目前主要的业务逻辑处理有四部分:
- 根据岸桥大车位置计算岸桥的UTM坐标,保存起来留待云端地图展示。
- 作业过程中,如果岸桥移动了且移动距离超过三米,重新给无人车下发指令,主要是将目的地改成移动后的位置
- 根据自动化设备状态以及引导值完成给无人车的引导
- 吊具尺寸变化要更新无人车的任务去重新对位
2、分析:
要针对岸桥自动化设备发来的数据做最起码四部分逻辑处理,还要有一些校验之类的处理,都写在一个类里显得这个类职责很不明确,且代码也会变多,不利于维护,之后再增加新的业务处理不仅使这个处理类进一步变臃肿,还可能会影响到其它部分。可维护性可扩展性都不好。结合责任链设计模式的特点来看,为每一个业务处理都单独定义一个类,使它们都去处理接收来的数据,每一块儿业务处理职责都很单一,利于维护,且将来增加新的业务逻辑,只需增加一个类即可。
为此定义一个抽象类AbstractQCHandler,以及四个子类:QCPositionSaveHandler,负责
保存岸桥坐标位置;QCPositionChangeHandler,负责岸桥位置变更的处理;QCAlignHandler,负责引导对位处理;QCSpreaderSizeChangeHandler,负责吊具尺寸变化处理。以及为实现链式调用,定义一个类QCHandlerChain,负责将各处理类进行调用。
3、代码:
3.1、抽象类 AbstractQCHandler:
@Slf4j
@Service
public abstract class AbstractQCHandler {
// 对外提供的处理方法,因为我需要在每个子类处理业务之前先做一部分校验等工作
//将真正的处理方法定义为抽象方法doHandle供各子类实现
public void handle(ECSStatusDTO ecsStatusDTO) {
// 省略部分校验以及前置处理工作
/.../
doHandle(ecsStatusDTO);
}
protected abstract void doHandle(ECSStatusDTO ecsStatusDTO);
}
3.2、岸桥位置保存处理类 QCPositionSaveHandler:
@Service("qcPositionSaveHandler")
public class QCPositionSaveHandler extends AbstractQCHandler {
@Override
public void doHandle(ECSStatusDTO ecsStatusDTO) {
//省略具体处理
}
}
3.3、岸桥位置变更处理类 QCPositionChangeHandler:
@Service("qcPositionChangeHandler")
@Slf4j
public class QCPositionChangeHandler extends AbstractQCHandler {
@Override
public void doHandle(ECSStatusDTO ecsStatusDTO) {
// 省略具体处理
}
}
3.4、岸桥对位处理类 QCAlignHandler:
@Service("qcAlignHandler")
public class QCAlignHandler extends AbstractQCHandler {
@Override
public void doHandle(ECSStatusDTO ecsStatusDTO) {
// 省略具体处理
}
}
3.5、岸桥吊具尺寸变化处理类 QCSpreaderSizeChangeHandler:
@Slf4j
@Service("qcSpreaderSizeChangeHandler")
public class QCSpreaderSizeChangeHandler extends AbstractQCHandler {
@Override
public void doHandle(ECSStatusDTO ecsStatusDTO) {
// 省略具体处理
}
}
3.6、链式调用类 QCHandlerChain:
@Service("qcHandlerChain")
public class QCHandlerChain {
@Resource
private List<AbstractQCHandler> qcHandlers;
public void chain(ECSStatusDTO ecsStatusDTO) {
for (AbstractQCHandler handler : qcHandlers) {
handler.handle(ecsStatusDTO);
}
}
}
至此,只需要在消费MQTT消息的时候调用这个链式处理类的chain方法即可,将原本臃肿的代码分散开来。如下:
@Slf4j
@Component("qcMessageConsumerHandler")
public class ECSQCMessageConsumerHandler implements MessageHandler {
@Resource(name = "qcHandlerChain")
private QCHandlerChain qcHandlerChain;
@Override
public void handleMessage(Message<?> message) throws MessagingException {
String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC, String.class);
Optional.of(message.getPayload())
.map(payload -> JsonUtil.parseObject((String) payload, ECSStatusDTO.class))
.ifPresent(ecsStatusDTO -> {
try {
qcHandlerChain.chain(ecsStatusDTO);
} catch (Exception e) {
log.error("处理QC消息失败,topic: {}, message: {}, error: {}", topic, message.getPayload(), ExceptionUtil.getSimpleStackTrace(e));
}
});
}
}
4、总结:
责任链设计模式侧重于扩展性,且由于责任链上的每一个对象的职责都很明确,所以也体现了单一职责原则,所带来的必然是良好的可维护性。之后遇到针对同一个请求,有很多业务处理的场景考虑使用责任链设计模式,好用!