Design-Pattern-Template
模板模式(Template Pattern)是一种行为型设计模式,其核心思想是:在抽象父类中定义一个操作的算法骨架(模板),将部分步骤的具体实现延迟到子类中完成。通过这种方式,子类可以在不改变算法整体结构的前提下,重新定义算法中的某些特定步骤。
场景
假设需要生成三种格式的报表(PDF/Excel/HTML),初始方案简单定义了三个独立类分别处理数据生成与报表渲染。
1 | |
对比三个类的代码,其中一部分代码是重复冗余的——所有报表的生成流程相同:
- 连接数据库获取数据
- 处理数据(过滤、排序、计算)
- 渲染为指定格式
- 输出文件
而客户端在调用生成器的方法时,不同生成器所属的类不同,因此可能要写多遍重复的调用代码。
1 | |
一个直观的改进思路是,先定义统一的生成器接口,再由各具体生成器类实现接口中的方法——这样客户端只需通过接口即可灵活引用各类具体生成器。不过,该方案仍无法解决三个具体生成器类中存在重复代码的问题。
模板模式
对于重复的代码和流程,可以将其抽取到一个抽象基类中。在上例中,就是将生成报表的过程,定义在抽象基类中。
1 | |
在抽取的流程代码中,定义了操作流程的框架,依次调用了四个抽象方法。将方法声明为抽象方法可以强制子类实现其功能,体现不同子类的差异化。如果大部分子类都没明显差别,也可以在基类中提供默认实现,仅在少数子类去重写。
模板模式包含两个核心角色,抽象模板类定义了算法的骨架(模板方法),包含一系列按顺序执行的步骤。其中,不变的步骤由抽象类自身实现,可变的步骤声明为抽象方法,由子类实现。通常会将模板方法声明为
final,防止子类重写算法的整体结构。具体子类实现抽象模板类中定义的抽象方法,为算法的可变步骤提供具体实现。
至此模板模式已介绍完毕,结构简单得没有绘制类图的必要。模板模式的应用,有一个很有意思的特点:它在实践中往往悄无声息地落地。当我们在代码库中发现多个类存在大量重复代码或相似执行逻辑时,自然会想到将这些共性内容抽取到抽象基类中,仅在子类里实现差异化的具体细节。完成这样的改造后,代码结构其实就已经符合模板模式的设计思想了。这一过程的本质,正是模板模式的典型应用场景。硬要细究这两者的差异,模板模式最核心的特征在于定义了一个算法框架,并在框架中依次调用了抽象方法。
在一些框架中,会有预留的、可在指定方法前后执行的可重写方法,称为钩子函数。例如在Servlet中重写init和destroy方法,以支持Servlet的初始化和销毁资源。这也是模板模式的一种实践。
在模板模式的具体实现类中,其对多个抽象方法的实现通常是互相绑定,配套使用的。例如上例中声明的获取数据、处理数据、生成报表、保存文件四个抽象方法,其在实现时不能任意组合。生成pdf的具体类定义的处理数据方法,不该能替换成生成Excel的具体类中的处理数据方法,因为方法的返回值会被作为生成报表过程的入参。而若抽象方法的实现也需要支持能够互相替换任意组合,则可以采取策略模式,直接由调用方传入,而不是在模板方法中在定义抽象方法。
总结
模板模式将多个类中相同的流程步骤抽取到抽象基类中,形成算法骨架。在算法骨架中调用多个抽象方法,并在具体子类中实现这些抽象方法。
如果算法骨架中的抽象方法也需要可以替换具体实现,则可以使用策略模式。