设计模式-模板模式

模板模式



什么是模板模式

  模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。这里的“算法”,我们可以理解为广义上的“业务逻辑”,并不特指数据结构和算法中的“算法”。这里的算法骨架就是“模板”,包含算法骨架的方法就是“模板方法”,这也是模板方法模式名字的由来。


为什么要用模板模式

  模板模式有两大作用:复用和扩展。其中,复用指的是,所有的子类可以复用父类中提供的模板方法的代码。扩展指的是,框架通过模板模式提供功能扩展点,让框架用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。
  我们在【设计模式-观察者模式】一文中提到过,可以通过实现ApplicationListener来完成对Spring全局事件的监听,代码如下:


public class ZfDtpRunInfoCollectEventLocalFileListener implements ApplicationListener<ZfDtpRunInfoCollectEvent> {
	@Override
    public final void onApplicationEvent(ZfDtpRunInfoCollectEvent event) {
        ZfDtpCollectTimeInfo zfDtpCollectTimeInfo = (ZfDtpCollectTimeInfo) event.getSource();
        
        // 获取采集信息
        String collectDay = zfDtpCollectTimeInfo.getCollectDay();

        // 保存线程池状态
        String localFilePath = CommonUtil.getFilePath(this.getLocalFilePathPrefix(), collectDay);
        ArrayList<String> appendLines = new ArrayList<>();
        appendLines.add(JSONUtil.toJsonStr(zfDtpCollectTimeInfo));
        FileUtil.appendLines(appendLines, localFilePath, StandardCharsets.UTF_8);
    }
}

  可以看到上面的代码中,我们实现了ApplicationListener接口,而这个接口中的onApplicationEvent方法入参为你自己定义的一个事件,需要通过

ZfDtpCollectTimeInfo zfDtpCollectTimeInfo = (ZfDtpCollectTimeInfo) event.getSource();

  来获取具体的信息,那么这一段代码实际上是每一个监听器都应该实现的,此时我们就可以通过模板模式来完成代码复用,代码示例如下:


// 抽象模板类
public abstract class ZfDtpAbstractRunInfoCollectEventListener implements ApplicationListener<ZfDtpRunInfoCollectEvent> {
    @Override
    public final void onApplicationEvent(ZfDtpRunInfoCollectEvent event) {
        ZfDtpCollectTimeInfo zfDtpCollectTimeInfo = (ZfDtpCollectTimeInfo) event.getSource();
        doCollect(zfDtpCollectTimeInfo);
    }
    protected abstract void doCollect(ZfDtpCollectTimeInfo zfDtpCollectTimeInfo);
}

// 具体实现类
public class ZfDtpRunInfoCollectEventLocalFileListener extends ZfDtpAbstractRunInfoCollectEventListener {
    private ZfDtpProperties zfDtpProperties;
    private String localFilePathPrefix;
    @Override
    public void doCollect(ZfDtpCollectTimeInfo zfDtpCollectTimeInfo) {
        // 获取采集信息
        String collectDay = zfDtpCollectTimeInfo.getCollectDay();

        // 保存线程池状态
        String localFilePath = CommonUtil.getFilePath(this.getLocalFilePathPrefix(), collectDay);
        ArrayList<String> appendLines = new ArrayList<>();
        appendLines.add(JSONUtil.toJsonStr(zfDtpCollectTimeInfo));
        FileUtil.appendLines(appendLines, localFilePath, StandardCharsets.UTF_8);
    }
}

  可以看到,我们优化后的代码是需要集成抽象类ZfDtpAbstractRunInfoCollectEventListener并重写doCollect方法保存我们采集的线程池信息即可。
  上面我们优化的代码其实同时兼备了模板模式的两个优点,一是代码复用,复用了将事件信息转换成我们需要的对象这一部分代码;二是易于拓展,只需要实现我们定义的抽象类,重写doCollect方法就能完成信息的保存。不需要考虑如何获取具体的事件与详细信息。


如何使用模板模式

  我们上面的代码中其实是一个非常简单的模板模式,定义一个抽象父类,将不需要子类复写的方法加上final,将需要子类实现的方法定义为抽象方法,我们用代码示例来看下:

// 抽象父类
public abstract class AbstractTemplate {
    public final void templateMethod(){
        step1Method();
        step2Method();
        step3Method();
    }

    abstract void step1Method();
    abstract void step2Method();
    abstract void step3Method();
}

// 具象子类
public class ChildClass extends AbstractTemplate{
    @Override
    void step1Method() {
        // 具体实现步骤一
    }

    @Override
    void step2Method() {
        // 具体实现步骤二
    }

    @Override
    void step3Method() {
        // 具体实现步骤三
    }
}

// 使用
public class TestTemplate {
    public static void main(String[] args) {
        AbstractTemplate abstractTemplate = new ChildClass();
        abstractTemplate.templateMethod();
    }
}

总结

  模板模式的用法和应用场景比较简单,我们在项目中应该比较常用,需要注意的是模板方法尽量使用final修饰避免子类重写导致模板失效。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值