设计模式之模板方法设计模式--在工作中的实际应用

模板方法设计模式侧重于代码复用,只需要在父类中定义一套处理流程,将其中的不变部分封装起来,可变的部分交由子类去实现即可。可提升代码的可读性、可维护性、灵活性。

1、背景:

公司的主营业务是港口无人驾驶,我的工作内容是云端部分,工作内容之一就是对接港口的自动化设备厂商,完成自动化作业。

1.1、名词解释:

岸桥:岸边桥式起重机,将集装箱从船上吊起放在无人车上或者反着来

场桥:堆场桥式起重机,将集装箱从无人车上吊起放在堆场或者反着来

码头面:岸桥作业场地

堆场:港口存放集装箱的场地

吊具:岸桥、场桥设备用来吊集装箱的部分设施

1.2、流程:

无人车接到指令去码头面做卸船装箱任务,到指令中指定的岸桥下作业,与岸桥上安装的自动化作业设备进行交互,等待吊具将集装箱放在无人车上。无人车上装好箱子后接到去堆场做卸船卸箱任务,到指令中指定的场桥下作业,与场桥上安装的自动化作业设备进行交互,等待吊具将集装箱从无人车上吊走。

无人车与岸桥场桥的自动化交互流程是一样的,只是各自执行对应流程的条件以及时机不一样,所以此时适合固定一套作业流程的模板,主流程固定,至于岸桥场桥各自触发交互的条件按实际情况去实现。为此定义一个抽象类名为AbstractEcsDataHandler;岸桥流程处理子类定义为ECSQCDataHandler,继承该抽象类;场桥流程处理子类定义为ECSRMGDataHandler,继承该抽象类。

2、代码:

抽象类:AbstractEcsDataHandler:

java 复制代码
public abstract class AbstractEcsDataHandler {
    public void handle(ECSStatusDTO ecsStatusDTO) {
        Optional.of(mappingVehicleAndEquipment())// 获取无人车与岸桥场桥设备的绑定关系,一个岸桥或者场桥可能对应多个无人车,key是vehicleId,代表车号,value是equipmentId代表岸桥场桥设备编号
                .map(Map::entrySet)
                .orElseGet(Collections::emptySet)
                .stream()
                .filter(entry -> StringUtils.equals(ecsStatusDTO.getCraneCode(), entry.getValue()))
                .map(Map.Entry::getKey)
                .map(vehicleId -> redisManager.get(RedisKeyConstants.SN_API_TASK_CACHE_EXEC_TASK + vehicleId))// 获取无人车任务指令字符串
                .map(cachedExecTaskStr -> JsonUtil.parseObject(cachedExecTaskStr, TaskDto.class))// 反序列化出任务dto
                .filter(Objects::nonNull)
                .filter(this::isCorrectTaskType) // 任务中有是岸桥任务还是场桥任务属性,交由两个子类去实现
                .filter(cachedExecTask -> isCorrectWorkPosition(ecsStatusDTO, cachedExecTask))// 任务中有作业目的地,岸桥场桥对目的地的定义不一致,交由两个子类去实现
                .filter(cachedExecTask -> {
                    // 判断是否收到自动化设备的解锁指令,交由两个子类实现
                    if (isNotUnLockCommand(ecsStatusDTO)) {
                        return true;
                    }
                    // 省略收到自动化设备解锁方法
                    log.info("xxxxxxxxx");
                    return false;
                })
                .filter(cachedExecTask -> {
                    if(xxx) {
                        // 省略此部分方法
                        return true;
                    }
                    log.info("xxxxxxxxx");
                    return false;					
                })
                .findFirst()
                .filter(cachedExecTask -> isNotLocked(cachedExecTask.getTruckId()))
                .filter(cachedExecTask -> {
                    // 判断是否收到自动化设备的锁车指令,交由两个子类实现
                    if (isNotLockCommand(ecsStatusDTO)) {
                        return true;
                    }
                    // 省略收到自动化设备的锁车处理
                    return false;
                })
                .filter(cachedExecTask -> {
                    // 判断是否对位完成,交由两个子类实现
                    if (isNotAlignFinish(ecsStatusDTO)) {
                        return true;
                    }
                    // 省略对位完成处理
                    return false;
                })
                .filter(cachedExecTask -> {
                    // 判断无人车是否需要对位,交由两个子类实现
                    if (vehicleNeedAlign(ecsStatusDTO)) {
                        return true;
                    }
                    // 省略不需要对位的处理
                    return false;
                })
                .ifPresent(cachedExecTask -> {
                    // 获取自动化设备下发的对位值,交由两个子类实现
                    int currentDistance = acquireAlignDistance(ecsStatusDTO);
                    // 省略对位值的处理
                });
    }

    /**
     * 车辆和要作业的ECS设备的映射关系 key: vehicleId value: qcId or rmgId
     */
    protected abstract Map<String, String> mappingVehicleAndEquipment();

    protected abstract boolean isCorrectTaskType(TaskDto task);

    protected abstract boolean isCorrectWorkPosition(ECSStatusDTO ecsStatusDTO, TaskDto task);

    protected abstract boolean isNotUnLockCommand(ECSStatusDTO ecsStatusDTO);

    protected abstract boolean isNotLockCommand(ECSStatusDTO ecsStatusDTO);

    protected abstract boolean isNotAlignFinish(ECSStatusDTO ecsStatusDTO);

    protected abstract boolean vehicleNeedAlign(ECSStatusDTO ecsStatusDTO);

    protected abstract int acquireAlignDistance(ECSStatusDTO ecsStatusDTO);
	
}

两个子类ECSQCDataHandler和ECSRMGDataHandler特别干净,只需要实现父类中的抽象方法即可,调用时直接使用handle()方法处理。

3、总结:

模板方法设计模式在一些固定业务的场景中特别适合,极大的省去了重复代码的编写成本以及维护成本,符合OCP原则,当一个子类中的方法有修改操作,不会影响其它子类对应的场景。在可读性方面,模板方法模式具备天然的易于理解的优势。之后遇到可能出现大量重复代码的时候,考虑模板方法设计模式。

相关推荐
晨米酱16 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机21 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机1 天前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤1 天前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机2 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机2 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴2 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤2 天前
工厂模式
设计模式