GoF设计模式——模板方法模式

GoF设计模式——模板方法模式 h5打开以查看为什么需要模板方法模式?假设正在做一个数据导出功能,需要支持 Excel、PDF、CSV 三种格式。每种格式的导出流程都是读数据 → 格式化 → 写文件 → 关闭资源,仅"格式化"这一步的实现不同。最直觉的写法是每个导出类各自写一遍完整流程:class ExcelExporter { public void export() { loadData(); // Excel 格式化 writeToExcel(); closeResource(); } // ... loadData / closeResource 的实现 ... } class PdfExporter { public void export() { loadData(); // PDF 格式化 writeToPdf(); closeResource(); } // ... loadData / closeResource 的实现 ... } class CsvExporter { public void export() { loadData(); // CSV 格式化 writeToCsv(); closeResource(); } // ... loadData / closeResource 的实现 ... }问题很明显:三个类里loadData和closeResource是完全一样的代码,但因为整体流程被写死在各自的export里,只能重复三遍。更糟的是——如果哪天要在"关闭资源"前加一步"生成审计日志",就得三个类都改一遍,一处忘改就翻车。这里真正的共用点不在"某一步",而在"这些步骤的执行顺序"。工具类能复用步骤,但复用不了流程——调用顺序仍散落在各导出类里,流程一变照样要改多处。模板方法模式解决的就是这个问题:把不变的流程骨架放到父类,把变化的具体步骤留给子类。概念模板方法模式(Template Method Pattern)是一种行为型设计模式,核心思想是定义一个算法的骨架,将某些步骤的实现延迟到子类。子类可以在不改变算法整体结构的情况下,重新定义算法中的某些特定步骤。可以把它想象成做菜的菜谱:每道菜的做法都包含备料 → 烹饪 → 装盘三步,顺序不能颠倒。菜谱模板固定了这个顺序,但"烹饪"具体怎么做因菜而异——红烧肉靠焖,清蒸鱼靠蒸,麻婆豆腐靠爆炒。菜谱不管你怎么烹,只管保证流程走完;具体的菜品负责往烹饪这一步里填内容。模板方法模式涉及两个角色:AbstractClass(模板类):定义算法骨架的抽象类,包含固定顺序的模板方法(通常用final修饰防止子类改动流程),并把可变步骤声明为抽象方法ConcreteClass(具体类):继承模板类,实现其中的抽象方法,为算法中的可变步骤提供具体逻辑模板方法内部包含三种基本方法:抽象方法:算法中必须由子类实现的可变步骤具体方法:算法中不变的通用步骤,父类直接实现,子类可选择重写钩子方法:算法骨架中的"插入点",父类给默认实现(通常为空或返回 true/false),子类可以通过重写钩子影响算法执行流程图中各类之间的关系:AbstractClass的templateMethod用final修饰,定义算法骨架——按固定顺序调用step1、commonMethod、step2、hookMethod等基本方法。其中step1、step2是抽象方法,子类必须实现;commonMethod是具体方法,父类已有默认实现;hookMethod是钩子方法,父类给默认空实现,子类按需重写——ConcreteClassA重写了钩子,ConcreteClassB没重写。实现模板方法模式的核心是父类掌控流程、子类填充细节——好莱坞原则(Hollywood Principle):"Don't call us, we'll call you"。子类不主动调用父类逻辑,而是被父类的模板方法在合适的时机反向调用。基础实现定义一个抽象类AbstractClass,其中templateMethod是模板方法,用final修饰确保子类不能改变算法骨架;step1、step2、step3是抽象方法,必须由子类实现;commonMethod是具体方法,已有默认实现;hookMethod是钩子方法,默认空实现供子类选择性重写。具体类ConcreteClass继承并实现所有抽象方法,同时可以选择重写钩子。// AbstractClass:模板类 abstract class AbstractClass { // 模板方法:定义算法骨架,final 防止子类破坏流程 public final void templateMethod() { step1(); commonMethod(); step2(); if (hookMethod()) { // 钩子决定是否执行 step3 step3(); } } // 抽象方法:必须由子类实现 protected abstract void step1(); protected abstract void step2(); protected abstract void step3(); // 具体方