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

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

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

相关推荐
怕浪猫20 小时前
领域特定语言(Domain-Specific Language, DSL)
设计模式·程序员·架构
Larcher3 天前
AI Loop:让AI像人一样自主完成任务的核心机制
javascript·人工智能·设计模式
咖啡八杯4 天前
GoF设计模式——享元模式
java·spring·设计模式·享元模式
:mnong4 天前
学习创建结构行为设计模式
设计模式
w_t_y_y4 天前
Agent设计模式(四)多模态融合模式(Multi-Modal Fusion)
设计模式
zhouhui0014 天前
订单状态的 if-else 地狱上线就崩——状态模式的工业级落地
设计模式
geovindu4 天前
go: Reactor Pattern
开发语言·后端·设计模式·golang·反应器模式
一只旭宝5 天前
【C++入门精讲22】常见设计模式
c++·设计模式
许彰午5 天前
38_Java设计模式之装饰器模式
java·设计模式·装饰器模式