模板方法模式介绍与示例

目录

一、模板方法模式简介

二、代码示例

 三、总结


一、模板方法模式简介

模板方法模式的核心设计思路是通过在抽象类中公开定义抽象方法的执行顺序,并将抽象方法的设定为只有子类去实现但不设计独立的访问方法。即子类实现的抽象方法不能被其它类访问,所有抽象方法的执行顺序逻辑由抽象类中的公开方法进行控制。

解决的问题:系统中一些业务场景的方法能够通用,每一个子类却都需要重新实现这一方法。

何时使用:有一些通用实现方法的时候就可以使用。

使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要或复杂的方法考虑作为模板方法。

二、代码示例

首先我们先大概看下业务模型:假设我们现在有导入excel表格的功能,但是导入的excel表格伴随着业务的区别各不相同,但是都属于这个系统。

为了提高效率,没有必要每个业务都去实现一套文件导入的逻辑,我们可以定义出一套Excel导入的业务逻辑模板,后续任何业务的Excel导入只需要继承定义好的模板去实现各自的方法即可。

下面我们就根据上面描述的业务场景来模拟一下实现过程:

1.业务dto:用户类和商品类;

@Data
public class UserImportDto {
    /**
     * 用户名称
     */
    private String name;
    /**
     * 家庭地址
     */
    private String message;
    /**
     * 年龄
     */
    private Integer age;

}
@Data
public class ProductDto {

    /**
     * 商品名称
     */
    private String productName;
    /**
     * 商品编码
     */
    private String productCode;
    /**
     * 商品使用信息
     */
    private String productInfo;
    /**
     * 商品价格
     */
    private BigDecimal price;
}

2.核心的抽象模板类:

/**
 * 不同业务的Excel表格导入公共实现模板;
 * 如果系统的所有excel导入都使用公共模板,除了业务不一样,导入的过程是一样的。
 */
@Slf4j
public abstract class ImportTemplate<T> {

    public Result importMethod(String fileName) {


        // 1.获取模板文件列头信息以及导入文件的列头信息
        String fileHeadTemplate = fileHeadTemplate();
        String columnHeads = getColumnHeads(fileName);

        // 2.校验导入数据的列头是否和模板匹配;
        Boolean checkHeads = checkFileColumnHead(fileHeadTemplate,columnHeads);
        if (!checkHeads) {
            return Result.err("导入文件列头与定义的表格列头不一致!");
        }

        // 3.若列头符合要求,获取表中数据
        List<T> dataList = getFileData(fileName);
        if (CollectionUtils.isEmpty(dataList)) {
            log.info("未获取到业务数据,导入失败!");
            return Result.err("未获取到导入的业务数据!");
        }

        // 4.导入的数据进行各自的业务校验
        Boolean ok = checkDataList(dataList);
        if (!ok) {
            return Result.err("数据业务校验失败!");
        }

        // 5.业务校验通过,保存数据
        saveData(dataList);

        return Result.ok("导入成功!");
    }

    /**
     * 需要导入的excel表格的列头样式;
     * @return
     */
    protected abstract String fileHeadTemplate();

    /**
     * 获取导入文件的列头;
     *
     * @return
     */
    protected abstract String getColumnHeads(String fileName);

    /**
     * 校验导入的列头是否和模板样例一致;
     *
     * @return
     */
    protected abstract Boolean checkFileColumnHead(String fileHeadTemplate,String columnHeads);

    /**
     * 获取导入数据
     *
     * @return
     */
    protected abstract List<T> getFileData(String fileName);

    /**
     * 对导入的数据进行各种业务校验
     *
     * @param dataList
     * @return
     */
    protected abstract Boolean checkDataList(List<T> dataList);

    /**
     * 校验后的数据导入到服务器或者记录到数据库中;
     * @param list
     */
    protected abstract void saveData(List<T> list);

}

3.模板实现类:用户Excel导入和商品Excel导入

@Slf4j
public class UserInfoImport extends ImportTemplate<UserImportDto> {

    @Override
    protected String fileHeadTemplate(){
        log.info("用户excel表格模板==={}","用户名称,家庭地址,年龄");
        return "用户名称,家庭地址,年龄";
    }

    @Override
    protected String getColumnHeads(String fileName) {
        // 此处省略实际获取用户导入文件列头信息的业务逻辑,非设计模式关注的重点;
        log.info("获取用户导入文件的表头==={}","用户名称,家庭地址,年龄");
        return "用户名称,家庭地址,年龄";
    }

    @Override
    protected Boolean checkFileColumnHead(String fileHeadTemplate,String columnHeads) {
        // 如果导入文件的列头和模板定义的列头一致,校验通过
        if(fileHeadTemplate.equals(columnHeads)){
            log.info("用户excel表格导入文件表头校验通过");
            return true;
        }
        return false;
    }

    @Override
    protected List<UserImportDto> getFileData(String fileName) {
        // 模拟从Linux文件服务器上获取导入文件的数据
        List<UserImportDto> list = new ArrayList<>();
        log.info("获取导入用户数据");
        // 业务代码省略。。。
        return list;
    }

    @Override
    protected Boolean checkDataList(List<UserImportDto> dataList) {
        // 对获取的数据进行业务校验,比如姓名必须填写;
        for(UserImportDto userImportDto : dataList){
            if(StringUtils.isEmpty(userImportDto.getName())){
                return false;
            }
        }
        log.info("导入的用户数据校验通过");
        return true;
    }

    @Override
    protected void saveData(List<UserImportDto> list) {
        // 模拟实际的保存用户数据业务逻辑
        // 数据入库或存储文件的代码省略...
        log.info("用户数据保存成功!");
    }
}
@Slf4j
public class ProductImport extends ImportTemplate<ProductDto> {

    @Override
    protected String fileHeadTemplate() {
        log.info("商品excel表格模板==={}","用户名称,家庭地址,年龄");
        return "商品名称,商品编码,商品使用信息,商品价格";
    }

    @Override
    protected String getColumnHeads(String fileName) {
        // 此处省略实际获取商品导入文件列头信息的业务逻辑,非设计模式关注的重点;
        log.info("获取商品导入文件的表头==={}","商品名称,商品编码,商品使用信息,商品价格");
        return "商品名称,商品编码,商品使用信息,商品价格";
    }

    @Override
    protected Boolean checkFileColumnHead(String fileHeadTemplate, String columnHeads) {
        // 如果导入文件的列头和模板定义的列头一致,校验通过
        if (fileHeadTemplate.equals(columnHeads)) {
            log.info("商品excel表格导入文件表头校验通过");
            return true;
        }
        return false;
    }

    @Override
    protected List<ProductDto> getFileData(String fileName) {
        // 模拟从专门的文件云服务器获取导入文件的数据
        List<ProductDto> list = new ArrayList<>();
        ProductDto productDto = new ProductDto();
        productDto.setProductName("娃哈哈");
        productDto.setProductCode("123");
        productDto.setProductInfo("可以喝的AD钙奶");
        productDto.setPrice(new BigDecimal("1"));
        list.add(productDto);
        log.info("获取导入商品数据");
        // 业务代码省略。。。
        return list;
    }

    @Override
    protected Boolean checkDataList(List<ProductDto> dataList) {
        // 对获取的数据进行业务校验,比如姓名必须填写;
        for (ProductDto userImportDto : dataList) {
            if (StringUtils.isEmpty(userImportDto.getProductCode())) {
                return false;
            }
        }
        log.info("导入的商品数据校验通过");
        return true;
    }

    @Override
    protected void saveData(List<ProductDto> list) {
        // 模拟实际的商品保存数据业务逻辑
        // 数据入库或存储文件的代码省略...
        log.info("商品数据保存成功!");
    }
}

4.测试类:

public class TemplateModelDemo {

    public static void main(String[] args) {
        System.out.println("模拟导入的用户表格中无用户信息");
        ImportTemplate userInfoImport = new UserInfoImport();
        userInfoImport.importMethod("xxxx");

        System.out.println("模拟导入有商品表格含有商品信息");
        ImportTemplate productImport = new ProductImport();
        productImport.importMethod("xxxx");
    }

}

5.测试结果:

 三、总结

通过上面的实现可以看到模板方法模式在定义统一的结构也就是执行标准方面非常的方便,能很好的做到让后续实现者不用关心调用逻辑,只需按照统一方式执行即可。

另外,模板方法模式也是为了解决子类通用方法,放到父类中进行优化设计。让每一个子类只做子类需要完成的事情,而不需要关心其它逻辑。

简单总结:行为由父类统一管理,可变扩展部分由子类各自实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值