模板模式

模板模式(Template Pattern)

       在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。也就是说在重复干一件事情的时候,在这些事情中主体的架构、骨架是一样的,只是每个事情有它自己的一些特性。就比如说我之前在开发君子签合同的时候,十几个合同。每隔合同的开发做的事情都是一样的。只是每个合同的内容,公司,章有所不同,但是合同的步骤都是一样的,这个时候,我们就可以使用模板模式。把签订合同的一系列步骤写一个抽象类,每个合同的类型这些,还有一些特性的参数就可以弄成抽象方法,让每个子类去重写。

特点:

1、一些方法通用,特性的东西让每个子类去重写。

2、将通用的算法、方法提取出来作为模板方法。

3、业务层调用的是模板方法。

4、关键代码在抽象类实现,其他步骤在子类实现。

优点: 

1、封装不变部分,扩展可变部分。

2、提取公共代码,便于维护。

3、行为由父类控制,子类实现。

缺点:

每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

使用场景: 

1、有多个子类共有的方法,且逻辑相同。

2、重要的、复杂的方法,可以考虑作为模板方法。

注意事项:

为防止恶意操作,一般模板方法都加上 final 关键词。

本人使用场景:在开发合同的时候,十几个合同每个合同的签订步骤都是一样的,我们就可以把签合同的这个事情作为一个模板方法,合同类型,合同上面的公司、法人、内容这些是不一样的,我们就可以把这些作为抽象的方法,让每个子类自己去实现自己特性的东西。

代码如下:

模板方法类:AbstractJunZiQianService

/**
*O和R是使用在类上面的泛型,O作为入参,R作为出参。因为公司是这样规定的,入参和出参都要放到实体里面
*/
public abstract class AbstractJunZiQianService<O extends JunZiQianBaseOrder, R extends JunZiQianBaseResult>
        extends JunZiQianClientService {
    private Logger log = LoggerFactory.getLogger(AbstractJunZiQianService.class);

    @Autowired
    private OrdOrderContractMapper ordOrderContractMapper;

    public R execute(O order) {
        // 初始化result(出参)
        R result = initResult();

        // 准备合同类型
        JunZiQianContractTypeEnum contract = initContractType();
        if (contract == null) {
            result.setStatus(Status.FAIL);
            result.setMessage("未设置合同类型!");
            return result;
        }

        // 设置mdc
        setMDC(order);
        // 校验参数
        order.check();

        log.info("收到[{}]合同签署请求,请求参数:{}", contract.message(), order);

        try {
            // 设置默认值
            if (result.getStatus() == null) {
                result.setStatus(Status.SUCCESS);
            }
            if (result.getMessage() == null) {
                result.setMessage("签署成功");
            }

            // 合同状态校验
            OrdOrderContract ordOrderContract = ordOrderContractMapper.findByOrderIdAndContractType(order.getOrderId(), contract.code());
            if (ObjectUtils.isEmpty(ordOrderContract)) {
                ordOrderContract = new OrdOrderContract();
                // 订单id
                ordOrderContract.setOrderId(order.getOrderId());
                // 合同名字
                ordOrderContract.setContractName(contract.message());
                // 合同类型
                ordOrderContract.setContractType(contract);
                // 合同编号
                ordOrderContract.setContractNo(AppUtils.newOrderNo());
            } else {
                log.warn("[{}]已签署,请不要重复签署", contract.message());
                throw new BizError("[" + contract.message() + "]已签署,请不要重复签署");
            }

            // 初始化请求头
            ApplySignTmplRequest.Builder builder = initBuilder(contract.message(), contract.id());
            // 根据合同设置个性化参数
            setBuilder(builder, order, ordOrderContract.getContractNo());
            // 创建客户端并发起请求,上线修改
            ApplySignResponse response = getTestClient().applySignTmpl(builder.build());
            //打印君子签日志
            LogUtils.logResponse(response);
            // 解析返回结果
            if (response.isSuccess()) {
                log.info("[{}]合同君子签成功", contract.message());
                //君子签合同申请编号
                ordOrderContract.setApplyNo(response.getApplyNo());
                //成功
                ordOrderContract.setContractStatus(StatusSwitchEnum.Y);
            } else {
                log.warn("[{}]合同君子签失败:{}", contract.message(), response.getError().getMessage());
                throw new BizError("[" + contract.message() + "]签署失败:" + response.getError().getMessage());
            }
            // 保存合同信息
            log.info("新增订单合同记录");
            ordOrderContractMapper.insertSelective(ordOrderContract);
            result.setContractNo(ordOrderContract.getContractNo());
        } catch (BizError error) {
            result.setStatus(Status.FAIL);
            result.setMessage(error.getMessage());
        } catch (Exception e) {
            result.setStatus(Status.FAIL);
            result.setMessage("系统繁忙,请稍后再试!");
            log.error("[{}]合同签署发生异常:", e);
        } finally {
            if (result.isSuccess()) {
                log.info("[{}]合同签署成功,结果:{}", contract.message(), result);
            } else if (result.isProcessing()) {
                log.info("[{}]合同签署中,结果:{}", contract.message(), result);
            } else {
                log.info("[{}]合同签署失败,原因:{},结果:{}", contract.message(), result.getMessage(), result);
            }
            //打印摘要日志
            MDC.clear();
        }
        return result;
    }
    //初始化出参
    protected abstract R initResult();
    //获取合同类型
    protected abstract JunZiQianContractTypeEnum initContractType();
    //封装每个合同的一些特性东西,相当于就是封装我这个合同要显示的公司,法人信息这些东西
    protected abstract void setBuilder(ApplySignTmplRequest.Builder builder, O order, String contractNo);

    /**
     * 设置MDC
     *
     * @param order
     */
    private void setMDC(O order) {
        if (order != null && StringUtils.isNotBlank(order.getSerialNo())) {
            if (StringUtils.isNotBlank(MDC.get("serialNo"))) {
                MDC.remove("serialNo");
            }
            MDC.put("serialNo", "-[serialNo:" + order.getSerialNo() + "]");
        }
    }
}

上面的这个类就是一个模板类,里面的execute()就是模板方法,所有的合同都是调用的这个生成方法。

然后看一下一个子类:DFB_ON_NOTDelegate 类

@Service
public class DFB_ON_NOTDelegate extends AbstractJunZiQianService<DFB_ON_NOTOrder, DFB_ON_NOTResult> {
    
    //每一个合同的返回值实体类是不同的
    @Override
    protected DFB_ON_NOTResult initResult() {
        return new DFB_ON_NOTResult();
    }

    //获取每个合同的类型
    @Override
    protected JunZiQianContractTypeEnum initContractType() {
        return JunZiQianContractTypeEnum.DFB_ON_NOT;
    }

    //
    @Override
    protected void setBuilder(ApplySignTmplRequest.Builder builder, DFB_ON_NOTOrder order, String contractNo) {
        // 4.构建模板动态参数
        Map<String, Object> params = Maps.newHashMap();
        params.put("contractNo", contractNo);//协议||合同编号
        params.put("b2CompanyName", order.getB2CompanyName());//公司名称
        params.put("contractDate", order.getContractDate());//合同签订日期
        params.put("contractNoOfDFB_ON_NOT", order.getContractNoOfPAS_ON_NOT());//合同编号
        params.put("b2LoanAmountCH", order.getB2LoanAmountCH());//转账金额大写
        params.put("b2LoanAmount", order.getB2LoanAmount());//转账金额小写
        params.put("loanRate", order.getLoanRate());//年化费率
        params.put("loanPeriod", order.getLoanPeriod());//期限(天)

        builder.withContractParams(params);

        // 5.构建签约对象
        HashSet<Signatory> signatories = Sets.newHashSet();
        // 追加法人&公司的章信息
        signatories.add(dphTestLegalSignatory());
        signatories.add(dphTestCompanySignatory());
        // 追加法人&公司的章信息
        signatories.add(jiaHuaTestLegalSignatory());
        signatories.add(jiaHuaTestCompanySignatory());
        builder.withSignatories(signatories);
    }
}

上面这个类就是每个合同自己的一些特性,params里面保存的就是合同上面要显示的东西

看一下对应的入参和出参

public class DFB_ON_NOTOrder extends JunZiQianBaseOrder {
    private static final long serialVersionUID = -8837739717648845992L;
    @NotBlank(message = "合同签订日期不能为空")
    private String contractDate;
    @NotBlank(message = "公司名称不允许为空")
    private String b2CompanyName;
    @NotBlank(message = "合同编号不能为空")
    private String contractNoOfPAS_ON_NOT;
    @NotBlank(message = "转账金额大写不允许为空")
    private String b2LoanAmountCH;
    @NotBlank(message = "转账金额小写不允许为空")
    private String b2LoanAmount;
    @NotBlank(message = "年化费率不允许为空")
    private String loanRate;
    @NotBlank(message = "期限(天)不能为空")
    private String loanPeriod;

    //get和set方法省略。。。
}
public class DFB_ON_NOTResult extends JunZiQianBaseResult {
    private static final long serialVersionUID = 9125891038971985688L;
}

出参和入参就算没有什么属性,也要新建一个实体类,不能直接返回什么String,int之类的。

就这样,一个模板模式的例子就搞定了,只不过这样写的弊端你也能够想象出来。如果我有10个合同,那么我就要新建10个子类去继承模板类,新建10个实体类作为入参类,新建10个实体类作为出参类,新建的类会很多。但是的话也与好处,就是提高了代码的复用性和重用性,也便于维护。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值