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

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

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原则,当一个子类中的方法有修改操作,不会影响其它子类对应的场景。在可读性方面,模板方法模式具备天然的易于理解的优势。之后遇到可能出现大量重复代码的时候,考虑模板方法设计模式。

相关推荐
九狼22 分钟前
Flutter + Riverpod +MVI 架构下的现代状态管理
设计模式
静水流深_沧海一粟17 小时前
04 | 别再写几十个参数的构造函数了——建造者模式
设计模式
StarkCoder17 小时前
从UIKit到SwiftUI的迁移感悟:数据驱动的革命
设计模式
阿星AI工作室1 天前
给openclaw龙虾造了间像素办公室!实时看它写代码、摸鱼、修bug、写日报,太可爱了吧!
前端·人工智能·设计模式
_哆啦A梦2 天前
Vibe Coding 全栈专业名词清单|设计模式·基础篇(创建型+结构型核心名词)
前端·设计模式·vibecoding
阿闽ooo5 天前
中介者模式打造多人聊天室系统
c++·设计模式·中介者模式
小米4965 天前
js设计模式 --- 工厂模式
设计模式
逆境不可逃5 天前
【从零入门23种设计模式08】结构型之组合模式(含电商业务场景)
线性代数·算法·设计模式·职场和发展·矩阵·组合模式
驴儿响叮当20105 天前
设计模式之状态模式
设计模式·状态模式
电子科技圈5 天前
XMOS推动智能音频等媒体处理技术从嵌入式系统转向全新边缘计算
人工智能·mcu·物联网·设计模式·音视频·边缘计算·iot