# 第2节.Nacos中的模板方法设计模式的使用

# 一、模板方法模式的定义

在父类定义一个操作中的算法骨架,而将算法的一些因具体情况而定的步骤延迟到子类中实现,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

# 二、模板方法模式的结构

抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。

模板方法:一个模板方法是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或一个总行为的方法。这个模板方法定义在抽象类中,并由子类不加以修改地完全继承下来。模板方法是一个具体方法,它给出了一个顶层逻辑框架,而逻辑的组成步骤在抽象类中可以是具体方法,也可以是抽象方法。由于模板方法是具体方法,因此模板方法模式中的抽象层只能是抽象类,而不是接口。

基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:

抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。

具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。

钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。

具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。

# 三、Nacos中模板方法模式的使用

# 3.1、Nacos的Distro异步任务

接口类:NacosTask

抽象类:AbstractExecuteTask、AbstractDistroExecuteTask

具体实现类:DistroSyncChangeTask、DistroSyncDeleteTask

其中,DistroSyncChangeTask和DistroSyncDeleteTask类负责,集群之间基于Distro协议的sync数据同步。AbstractDistroExecuteTask类中定义程序的流程,同时暴露了doExecute和doExecuteWithCallback方法,核心代码如下:

public abstract class AbstractDistroExecuteTask extends AbstractExecuteTask {
    
    private final DistroKey distroKey;
    
    private final DistroComponentHolder distroComponentHolder;
    
    protected AbstractDistroExecuteTask(DistroKey distroKey, DistroComponentHolder distroComponentHolder) {
        this.distroKey = distroKey;
        this.distroComponentHolder = distroComponentHolder;
    }
    
    @Override
    public void run() {
        String type = getDistroKey().getResourceType();
        DistroTransportAgent transportAgent = distroComponentHolder.findTransportAgent(type);
        if (null == transportAgent) {
            Loggers.DISTRO.warn("No found transport agent for type [{}]", type);
            return;
        }
        Loggers.DISTRO.info("[DISTRO-START] {}", toString());
        if (transportAgent.supportCallbackTransport()) {
            doExecuteWithCallback(new DistroExecuteCallback());
        } else {
            executeDistroTask();
        }
    }
    
    private void executeDistroTask() {
        try {
            boolean result = doExecute();
            if (!result) {
                handleFailedTask();
            }
            Loggers.DISTRO.info("[DISTRO-END] {} result: {}", toString(), result);
        } catch (Exception e) {
            Loggers.DISTRO.warn("[DISTRO] Sync data change failed.", e);
            handleFailedTask();
        }
    }
    
    protected abstract DataOperation getDataOperation();
    
    protected abstract boolean doExecute();
    
    protected abstract void doExecuteWithCallback(DistroCallback callback);
    
  	......省略部分代码
}

其中,DistroSyncChangeTask类的核心代码如下,一般当用到模板方法设计模式的时候,都会在子类中定义名称或者类型的方法,标识自己是谁。

public class DistroSyncChangeTask extends AbstractDistroExecuteTask {
    
    private static final DataOperation OPERATION = DataOperation.CHANGE;
    
    public DistroSyncChangeTask(DistroKey distroKey, DistroComponentHolder distroComponentHolder) {
        super(distroKey, distroComponentHolder);
    }
    
  	/**
  	* 子类定义一个类型返回
  	*/
    @Override
    protected DataOperation getDataOperation() {
        return OPERATION;
    }
    
  	/**
  	* 实现父类的抽象方法,通常以do开头
  	*/
    @Override
    protected boolean doExecute() {
        String type = getDistroKey().getResourceType();
        DistroData distroData = getDistroData(type);
        if (null == distroData) {
            Loggers.DISTRO.warn("[DISTRO] {} with null data to sync, skip", toString());
            return true;
        }
      	// 策略模式获取传输协议 同步数据
        return getDistroComponentHolder().findTransportAgent(type)
                .syncData(distroData, getDistroKey().getTargetServer());
    }
    
  	/**
  	* 实现父类的抽象方法,通常以do开头
  	*/
    @Ove
    @Override
    protected void doExecuteWithCallback(DistroCallback callback) {
        String type = getDistroKey().getResourceType();
        DistroData distroData = getDistroData(type);
        if (null == distroData) {
            Loggers.DISTRO.warn("[DISTRO] {} with null data to sync, skip", toString());
            return;
        }
      	// 策略模式获取传输协议 同步数据
        getDistroComponentHolder().findTransportAgent(type)
                .syncData(distroData, getDistroKey().getTargetServer(), callback);
    }
    
   	 
    private DistroData getDistroData(String type) {
        DistroData result = getDistroComponentHolder().findDataStorage(type).getDistroData(getDistroKey());
        if (null != result) {
            result.setType(OPERATION);
        }
        return result;
    }
}
Last Updated: 10/3/2022, 5:50:00 PM