☰
前端精读:设计模式之 Template Method 模版模式——固定算法骨架,让子类只改细节
2026/10/3 7:15:41 网站建设 项目流程
  • 文档
  • 技术博客
  • 教程

【免费下载链接】weekly

前端精读周刊。帮你理解最前沿、实用的技术。

项目地址:https://gitcode.com/GitHub_Trending/we/weekly
点击查看免费下载

Template Method(模版模式)属于行为型模式,核心思想是在父类中定义算法的骨架,把可变步骤延迟到子类实现。本文结合前端精读周刊设计模式系列的实践视角,先用模版文件、实例化、Vue 模版三个生活化例子建立直觉,再给出 TypeScript 可运行示例、结构拆解与适用边界。读完你将掌握如何提炼"通用算法 + 可重写步骤",在类、HTML 与脚手架三个层面正确运用模版模式,并分清它与策略模式的本质差异。

意图:定义骨架,延迟细节

意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模版模式的初衷是面向对象的:先定义一个父类,把一段通用算法写在其中,再将需要被子类重载的方法显式提取出来。子类只需重载这些"部分方法",即可复用父类已经实现好的整体算法。

最简单的形式:

function a() { b() + c() }

父类方法a已经定义了"b 与 c 相加"这个算法的骨架,子类只需要重定义b与c两个方法,就能复用a的算法。例子虽简单,但当算法本身较为复杂时,模版模式的好处就会凸显:算法结构(执行顺序、边界处理、公共逻辑)只写一遍,变化的只是其中少数几个"插槽"。

三个例子:什么场景会用到模版模式

设计模式需要在日常工作里用起来。下面三个例子帮助体会模版模式的典型应用场景。

模版文件:固化不变,填写变化

我们办事打印的文件就是模版文件,只需要写上个人基本信息再签字即可,不必做大量重复劳动——因为某些场景下大部分内容是可以固化下来的。比如买卖房屋,甲方乙方的条款基本固定,最大的变化只是甲方与乙方的不同。我们在模版上签字时,就是利用模版模式减少了大量写条款的时间。

这类场景的共性:骨架(条款结构)是稳定的,变化点(身份信息)是少数,把稳定部分写成模版、变化部分留给使用者填写,是最自然的模版模式应用。

实例化:用少量代码撬动大块功能

实例化也可以看作模版模式的某种表现形式。以工厂方法为例,传入不同的初始值可能得到不同结果,本质上就是用很少的代码撬动了很大一块功能,起到了抽象作用。当工厂的产品结构一致、只是参数不同时,"生产流程"就是一个固定的算法骨架,具体实例的差异被收敛到参数或子类实现上。

Vue 模版:最符合直觉的模版

Vue 模版更符合我们对模版直觉的理解。这个场景中的模版指 HTML 模版,我们只需要在模版中以{}(或插值语法)形式描述一些变量,就可以生成一块只有局部变量变化的模版 DOM,非常方便。页面结构的骨架固定,变化的只是被插值表达式标记出的局部内容——这正是"算法骨架固定、部分步骤可变"思想在声明式 UI 层的体现。

结构:ConcreteClass 与 primitiveOperation

模版模式的结构围绕一个"具体的父类"展开:

  • ConcreteClass:具体的父类。父类中实现了TemplateMethod,其内部调用了primitiveOperation1与primitiveOperation2两个原语操作,子类只需要重载这两个方法,即可享用TemplateMethod提供的完整算法。

用文档场景类比:假设TemplateMethod是OpenDocument(打开文档),那么primitiveOperation1可能是CanOpen(打开前校验),primitiveOperation2可能是ReadDocument(读取文档内容)。父类负责编排"校验 → 读取"的完整流程,子类只实现这两个具体环节。

使用体验上,调用方只要专心实现具体的细节方法,而不需要关心它们之间如何相互作用——父类会帮我们编排好。之后直接调用子类的OpenDocument即可完成打开文档的整体流程。

代码例子:TypeScript 实现

下面例子使用 TypeScript 编写:

class View { doDisplay(){} display() { this.setFocus() this.doDisplay() this.resetFocus() } } class MyView extends View { doDisplay(){ console.log('myDisplay') } } const myView = new MyView() myView.display()

这里doDisplay表示父类希望子类重载的方法,一般以do约定打头。display则是父类定义好的算法骨架:聚焦 → 显示 → 重置焦点。子类MyView只重写了doDisplay,就能获得完整的显示流程,且setFocus、resetFocus等公共环节对所有视图保持一致。

更完整的写法可以加上类型约束,让骨架更安全:

abstract class View { // 原语操作:父类不实现,交给子类 protected abstract doDisplay(): void // 模版方法:定义算法骨架,不可被子类重写 public display(): void { this.setFocus() this.doDisplay() this.resetFocus() } private setFocus(): void { console.log('set focus') } private resetFocus(): void { console.log('reset focus') } } class MyView extends View { protected doDisplay(): void { console.log('myDisplay') } } new MyView().display()

在这个变体中:

  • abstract强制子类实现doDisplay,漏写会在编译期报错;
  • setFocus、resetFocus声明为private,锁死公共环节,防止子类误改骨架;
  • 骨架方法display不声明为virtual/开放重载,保证"结构不改变"。

弊端与适用边界

模版模式用在类中,本质上是固定不可变的结构,进一步缩小重写方法的范围——重写的范围越小,代码可复用度就越高。因此必须遵循两个原则:

  1. 在具有通用算法可提取的情况下使用,而不要为了节省代码行数而过度使用。如果各子类之间根本没有可复用的骨架,强行抽象只会增加继承层级与理解成本。
  2. 不要固化思维在"类"这个框子里。模版模式写于 1994 年,其中的模式已被大量迁移运用。前端开发中 HTML 本身就非常契合模版模式——HTML 中有大量标签描述千变万化的 UI 结构,可复用的地方太多,所以非常适合模版思想;模版模式还能用在脚手架中,比如填入一些表单自动生成代码。能否识别并做适当的知识迁移,是今天学习设计模式的关键。

判断是否该用模版模式的信号:一段流程多次出现、步骤顺序基本不变、只有个别环节的实现不同。此时把流程提炼为骨架,把差异环节留作可重写点。

与策略模式的关系:改变一部分 vs 改变全部

模版模式与策略模式有一定相似处,都是"行为型、围绕算法变化"的模式,但关注点不同:

  • 模版模式:只改变算法的一部分。算法骨架由父类固定,子类重写其中少数步骤,整体结构不可变。
  • 策略模式:将策略完全提取出来,所以可以改变算法的全部。策略模式定义一系列算法并封装,使它们可相互替换,算法可独立于使用它的客户而变化。

简言之,当"流程骨架稳定、仅局部细节多变"时倾向模版模式;当"整个算法都可能被替换"时倾向策略模式。两者也可以组合:骨架中的某个步骤委托给策略对象,把"部分可换"升级为"部分可任意换"。

延伸阅读

  • Strategy 策略模式:模版模式与策略模式的对比姊妹篇;
  • Factory Method 工厂方法:"实例化也是模版模式的表现形式"这一论断的详细展开;
  • Prototype 原型模式:模版组件与克隆思想的另一种设计模式解法。

以上文章均收录于前端精读周刊的设计模式模块,与本文共同构成设计模式的系列解读,可对照学习各模式之间的异同与组合方式。

  • 文档
  • 技术博客
  • 教程

【免费下载链接】weekly

前端精读周刊。帮你理解最前沿、实用的技术。

项目地址:https://gitcode.com/GitHub_Trending/we/weekly
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询