泛型+模板方法模式—解决父类方法签名不满足子类需求的问题

使用模板方法模式时,父类会抽取大量的公共逻辑。子类只需要实现个性化需求即可。

重写规则.png

难点:当某个子类在父类的钩子方法A中查询出X字段,需要传递到父类的钩子方法B中。但是方法的参数对象中没有X字段。

难点在于:子类需要修改父类参数列表,但是不符合重写规则,在编译的时候会失败。

解决方案:

  1. 去修改父类dto对象。(不满足开闭原则,且其他子类不需要X字段,会造成父类dto太冗余);
  2. 使用ThreadLocal传递。(契约性低,无法约束,容易忘记);
  3. 多态+引用传递,子类在钩子方法A中创建dto子类对象,向下传递,在钩子方法B中强转。(可读性差);

有没有一种更加巧妙的实现方式?

有的,就是利用泛型的语法来进行实现。这样父类公共逻辑可以使用Dto对象,子类个性化对象可以去扩展Dto对象。

代码参考:

/**
 * T 是PublishReq类或子类: 前端发送的请求对象;
 * V 是PublishDto类或子类:在"预处理器"中T会转换为V,V会在"持久化处理器"中完成入库等操作;
 * R 是PublishResp类或子类:对外输出;
 */
public interface PublishService {

    /**
     * 任务发布
     */
    R publish(T req, R resp);

}

抽象父类抽取公共逻辑:

/**
 * 模板方法模式,注意:模板父类定义的所有方法入参、出参均是泛型类型。
 * 这就保证了子类扩展时,可以使用个性化的实现。
 *
 * 注:方法出参为void,而值改变依赖的是"参数的 引用传递"来完成变化。
 */
public abstract class AbstractPublishService implements PublishService {

    /**
     * 发布事件的公共逻辑...
     */
    @Override
    public R publish(T req, R resp) {
        //1. 校验逻辑
        validatorHandler(req, resp);
        //2. 预处理器
        V dto = preHandler(req, resp);
        //3. 持久化处理器
        persistenceHandler(req, dto, resp);
        //4. 后置处理器
        postHandler(req, dto, resp);
        return resp;
    }

    /*************************************************************
     * 1. 校验处理器(校验T)
     ************************************************************/
    protected void validatorHandler(T req, R resp) {
        //公共校验
        if (req.getTaskName() == null) {
            throw new IllegalArgumentException("taskName is not null");
        }
        //个性化校验的钩子方法
        customValidatorHandler(req, resp);
    }

    /**
     * 子类需要实现个性化校验逻辑的钩子方法
     */
    protected void customValidatorHandler(T req, R resp) {
    }

    /*************************************************************
     * 2. 预处理器(T转换为将要入库的V)
     ************************************************************/
    protected V preHandler(T req, R resp) {
        //查询数据库,填充V对象
        V dto = buildPublishDto(req, resp);
        //填充公共字段(查询数据库)
        dto.setPlanName("练习");
        //个性化逻辑
        customPreHandler(req, dto, resp);
        return dto;
    }

    /**
     * 构建Dto对象
     */
    protected abstract V buildPublishDto(T req, R resp);

    /**
     * 子类需要实现个性化逻辑的钩子方法
     */
    protected void customPreHandler(T req, V dto, R resp) {
    }

    /*************************************************************
     * 3. 持久化处理器(V入库)
     ************************************************************/

    protected void persistenceHandler(T req, V dto, R resp) {
        //公共的持久化逻辑
        //todo
        customPersistenceHandler(req, dto, resp);
    }

    protected void customPersistenceHandler(T req, V dto, R resp) {
    }

    /*************************************************************
     * 4. 持久化处理器(R处理,消息事件等...)
     ************************************************************/
    protected void postHandler(T req, V dto, R resp) {
        //发布成功后,发布事件消息...
    }
}

子类个性化参数对象:

/**
 * 可以看到, T为WordPublishReq,V为WordPublishDto。子类重写的方法签名已经为个性化的。由此可以灵活的扩展。
 */
public class WordPublishService extends AbstractPublishService {

    /**
     * 对象转换,将WordPublishReq转换为要持久化的WordPublishDto对象、
     *
     */
    @Override
    protected WordPublishDto buildPublishDto(WordPublishReq req, PublishResp resp) {
        return null;
    }

    /**
     * 个性化持久化处理
     */
    @Override
    protected void customPersistenceHandler(WordPublishReq req, WordPublishDto dto, PublishResp resp) {
        super.customPersistenceHandler(req, dto, resp);
        //个性化持久化处理
    }
}

你可能感兴趣的:(泛型+模板方法模式—解决父类方法签名不满足子类需求的问题)