您现在的位置是:网站首页 > 模板方法模式(Template Method)的算法骨架定义文章详情

模板方法模式(Template Method)的算法骨架定义

模板方法模式(Template Method)的算法骨架定义

模板方法模式是一种行为设计模式,它在父类中定义了一个算法的框架,允许子类在不改变算法结构的情况下重写特定步骤。这种模式的核心在于将不变的部分封装在父类,可变的部分通过子类实现。

模式结构与角色

模板方法模式包含两个主要角色:

  1. 抽象类(Abstract Class):定义算法骨架和模板方法,包含基本操作的抽象声明和具体实现
  2. 具体类(Concrete Class):实现抽象类中定义的抽象操作,完成算法中与特定子类相关的步骤
// 抽象类
class AbstractClass {
  // 模板方法,定义算法骨架
  templateMethod() {
    this.primitiveOperation1();
    this.primitiveOperation2();
    this.concreteOperation();
    this.hook();
  }

  // 基本操作,子类必须实现
  primitiveOperation1() {
    throw new Error("必须实现primitiveOperation1方法");
  }

  primitiveOperation2() {
    throw new Error("必须实现primitiveOperation2方法");
  }

  // 具体操作,已有实现
  concreteOperation() {
    console.log("抽象类中的具体操作");
  }

  // 钩子方法,子类可选择覆盖
  hook() {}
}

// 具体类
class ConcreteClass extends AbstractClass {
  primitiveOperation1() {
    console.log("具体类A实现的operation1");
  }

  primitiveOperation2() {
    console.log("具体类A实现的operation2");
  }
}

JavaScript实现特点

在JavaScript中实现模板方法模式有其特殊性:

  1. JavaScript没有抽象类的原生支持,需要通过抛出错误模拟抽象方法
  2. 原型继承机制使得模式实现更加灵活
  3. 可以使用高阶函数替代类继承实现类似效果
// 使用高阶函数实现模板方法
function createBeverage(brew, addCondiments) {
  return {
    prepareRecipe() {
      this.boilWater();
      brew();
      this.pourInCup();
      addCondiments && addCondiments();
    },
    boilWater() {
      console.log("把水煮沸");
    },
    pourInCup() {
      console.log("倒入杯子");
    }
  };
}

// 创建具体饮料
const coffee = createBeverage(
  () => console.log("用沸水冲泡咖啡"),
  () => console.log("加糖和牛奶")
);

实际应用场景

模板方法模式在前端开发中有广泛的应用场景:

表单验证流程

class FormValidator {
  validate(formData) {
    this.beforeValidation();
    const errors = this.doValidate(formData);
    this.afterValidation();
    return errors;
  }

  beforeValidation() {
    console.log("验证前准备...");
  }

  // 抽象方法,子类实现具体验证逻辑
  doValidate() {
    throw new Error("必须实现doValidate方法");
  }

  afterValidation() {
    console.log("验证后处理...");
  }
}

class LoginFormValidator extends FormValidator {
  doValidate(formData) {
    const errors = [];
    if (!formData.username) {
      errors.push("用户名不能为空");
    }
    if (!formData.password) {
      errors.push("密码不能为空");
    }
    return errors;
  }
}

组件生命周期管理

React类组件的生命周期方法就是模板方法模式的典型应用:

class ReactComponent {
  componentDidMount() {}
  shouldComponentUpdate() {}
  componentDidUpdate() {}
  componentWillUnmount() {}
  
  // 模板方法
  render() {
    this.beforeRender();
    const content = this.doRender();
    this.afterRender();
    return content;
  }
  
  beforeRender() {}
  doRender() {
    throw new Error("必须实现doRender方法");
  }
  afterRender() {}
}

钩子方法的使用技巧

钩子方法是一种可选覆盖的方法,它为模板方法提供额外的扩展点:

class DataProcessor {
  process(data) {
    this.beforeProcessing(data);
    const result = this.doProcessing(data);
    this.afterProcessing(result);
    return result;
  }

  beforeProcessing(data) {
    // 钩子方法,默认空实现
  }

  doProcessing(data) {
    throw new Error("必须实现doProcessing方法");
  }

  afterProcessing(result) {
    // 钩子方法,默认空实现
  }
}

class CSVProcessor extends DataProcessor {
  beforeProcessing(data) {
    console.log("开始处理CSV数据");
  }

  doProcessing(data) {
    return data.split("\n").map(row => row.split(","));
  }
}

模式优势与适用场景

模板方法模式特别适用于以下场景:

  1. 多个类包含几乎相同的算法,只有某些细微差别
  2. 需要控制子类扩展点,确保核心算法不被修改
  3. 存在固定的处理流程,但具体步骤实现可能不同
// 支付流程示例
class PaymentProcessor {
  processPayment(amount) {
    this.validatePayment(amount);
    const paymentResult = this.executePayment(amount);
    this.notifyUser(paymentResult);
    return paymentResult;
  }

  validatePayment(amount) {
    if (amount <= 0) throw new Error("金额必须大于0");
  }

  executePayment() {
    throw new Error("必须实现executePayment方法");
  }

  notifyUser(result) {
    console.log(`支付结果: ${result.status}`);
  }
}

class CreditCardPayment extends PaymentProcessor {
  executePayment(amount) {
    console.log(`通过信用卡支付${amount}元`);
    return { status: "success", amount };
  }
}

class AlipayPayment extends PaymentProcessor {
  executePayment(amount) {
    console.log(`通过支付宝支付${amount}元`);
    return { status: "success", amount };
  }
}

与策略模式的比较

模板方法模式和策略模式都用于封装算法,但有以下区别:

  1. 模板方法使用继承,策略模式使用组合
  2. 模板方法定义算法骨架,策略模式定义完整算法
  3. 模板方法在编译时确定结构,策略模式在运行时切换
// 策略模式实现对比
const paymentStrategies = {
  creditCard: amount => ({ status: "success", amount }),
  alipay: amount => ({ status: "success", amount })
};

function processPayment(strategy, amount) {
  if (amount <= 0) throw new Error("金额必须大于0");
  const result = strategy(amount);
  console.log(`支付结果: ${result.status}`);
  return result;
}

JavaScript特有的实现变体

在JavaScript中,可以利用闭包和工厂函数实现更灵活的模板方法:

function createDataExporter(formatData, afterExport = () => {}) {
  return {
    export(data) {
      console.log("准备导出数据...");
      const formatted = this.validate(data) && formatData(data);
      this.writeToFile(formatted);
      afterExport();
      console.log("导出完成");
    },
    validate(data) {
      return Array.isArray(data) && data.length > 0;
    },
    writeToFile(data) {
      console.log(`写入文件: ${JSON.stringify(data)}`);
    }
  };
}

const jsonExporter = createDataExporter(
  data => JSON.stringify(data, null, 2),
  () => console.log("JSON导出后处理")
);

处理异步操作

现代前端开发中经常需要处理异步操作,模板方法模式也可以适应:

class AsyncDataLoader {
  async load() {
    try {
      await this.beforeLoad();
      const data = await this.fetchData();
      const processed = await this.processData(data);
      await this.afterLoad(processed);
      return processed;
    } catch (error) {
      await this.handleError(error);
      throw error;
    }
  }

  async beforeLoad() {}
  async fetchData() {
    throw new Error("必须实现fetchData方法");
  }
  async processData(data) {
    return data;
  }
  async afterLoad(data) {}
  async handleError(error) {
    console.error("加载错误:", error.message);
  }
}

class UserLoader extends AsyncDataLoader {
  async fetchData() {
    const response = await fetch("/api/users");
    return response.json();
  }

  async processData(users) {
    return users.map(user => ({
      ...user,
      fullName: `${user.firstName} ${user.lastName}`
    }));
  }
}

浏览器环境中的实际案例

浏览器API封装是模板方法模式的常见应用:

class AnalyticsTracker {
  track(event, data) {
    if (!this.shouldTrack(event)) return;
    
    const formattedData = this.formatData(data);
    this.sendToAnalytics(formattedData);
    this.logLocal(event, formattedData);
  }

  shouldTrack(event) {
    return true; // 默认跟踪所有事件
  }

  formatData(data) {
    return {
      ...data,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent
    };
  }

  sendToAnalytics(data) {
    throw new Error("必须实现sendToAnalytics方法");
  }

  logLocal(event, data) {
    console.log(`[Analytics] ${event}`, data);
  }
}

class GA4Tracker extends AnalyticsTracker {
  sendToAnalytics(data) {
    // 实际实现会使用gtag等
    console.log("发送数据到GA4:", data);
  }

  shouldTrack(event) {
    return !event.startsWith("debug_");
  }
}

测试中的模板方法

模板方法模式可以简化测试代码的编写:

class TestCase {
  run() {
    this.setUp();
    try {
      this.test();
      console.log("测试通过");
    } catch (error) {
      console.error("测试失败:", error.message);
    } finally {
      this.tearDown();
    }
  }

  setUp() {}
  test() {
    throw new Error("必须实现test方法");
  }
  tearDown() {}
}

class UserTest extends TestCase {
  setUp() {
    this.user = new User("John");
  }

  test() {
    if (this.user.name !== "John") {
      throw new Error("用户名不正确");
    }
  }

  tearDown() {
    this.user = null;
  }
}

与函数式编程的结合

在函数式编程范式下,模板方法可以通过高阶函数实现:

function withLogging(wrappedFunction) {
  return function(...args) {
    console.log(`开始执行: ${wrappedFunction.name}`);
    try {
      const result = wrappedFunction.apply(this, args);
      console.log("执行成功");
      return result;
    } catch (error) {
      console.error("执行失败:", error.message);
      throw error;
    }
  };
}

// 使用示例
const fetchWithLogging = withLogging(async function fetchData(url) {
  const response = await fetch(url);
  return response.json();
});

前端框架中的应用模式

现代前端框架中的许多概念都体现了模板方法模式的思想:

// Vue组件示例
const FormMixin = {
  methods: {
    submit() {
      if (!this.validate()) return;
      
      this.beforeSubmit();
      this.submitForm()
        .then(() => this.onSuccess())
        .catch(error => this.onError(error));
    },
    validate() {
      throw new Error("必须实现validate方法");
    },
    beforeSubmit() {},
    submitForm() {
      throw new Error("必须实现submitForm方法");
    },
    onSuccess() {
      this.$emit("success");
    },
    onError(error) {
      console.error("提交错误:", error);
    }
  }
};

// 使用mixin
Vue.component("UserForm", {
  mixins: [FormMixin],
  methods: {
    validate() {
      return this.username && this.password;
    },
    submitForm() {
      return axios.post("/api/users", {
        username: this.username,
        password: this.password
      });
    }
  }
});

性能优化考虑

在使用模板方法模式时,需要注意以下性能问题:

  1. 避免在模板方法中执行不必要的操作
  2. 考虑将钩子方法的空实现改为null检查
  3. 对于性能关键路径,可以内联部分操作
class OptimizedProcessor {
  process(data) {
    // 内联简单操作而不是调用方法
    if (data == null) throw new Error("数据不能为空");
    
    const start = performance.now();
    const result = this.doProcess(data);
    const duration = performance.now() - start;
    
    // 只有需要时才调用钩子
    this.onProcessed && this.onProcessed(result, duration);
    return result;
  }

  doProcess(data) {
    throw new Error("必须实现doProcess方法");
  }
}

设计原则体现

模板方法模式体现了多个面向对象设计原则:

  1. 好莱坞原则:"不要调用我们,我们会调用你" - 子类不直接调用父类
  2. 开闭原则 - 对扩展开放,对修改关闭
  3. 单一职责原则 - 每个类只关注自己的特殊逻辑
class ReportGenerator {
  generate() {
    const data = this.fetchData();
    const processed = this.processData(data);
    const formatted = this.formatReport(processed);
    return this.outputReport(formatted);
  }

  fetchData() {
    throw new Error("必须实现fetchData方法");
  }

  processData(data) {
    // 默认不处理
    return data;
  }

  formatReport(data) {
    throw new Error("必须实现formatReport方法");
  }

  outputReport(report) {
    console.log(report);
    return report;
  }
}

复杂流程管理

对于复杂流程,模板方法可以帮助维护清晰的执行顺序:

class OrderFulfillment {
  async fulfill(order) {
    await this.validateOrder(order);
    const payment = await this.processPayment(order);
    await this.prepareOrder(order);
    const shipment = await this.shipOrder(order);
    await this.notifyCustomer(order, shipment);
    return this.recordFulfillment(order);
  }

  async validateOrder(order) {
    if (!order.items || order.items.length === 0) {
      throw new Error("订单中没有商品");
    }
  }

  async processPayment(order) {
    throw new Error("必须实现processPayment方法");
  }

  async prepareOrder(order) {
    console.log(`准备订单 #${order.id}`);
  }

  async shipOrder(order) {
    throw new Error("必须实现shipOrder方法");
  }

  async notifyCustomer(order, shipment) {
    console.log(`通知客户订单 #${order.id}已发货`);
  }

  async recordFulfillment(order) {
    console.log(`记录订单 #${order.id}完成情况`);
    return { success: true };
  }
}

与其它模式的协同

模板方法模式常与其它模式结合使用:

  1. 工厂方法模式:模板方法中的某些步骤可以使用工厂方法创建对象
  2. 观察者模式:在钩子方法中实现发布/订阅机制
  3. 命令模式:将某些步骤封装为命令对象
class DocumentProcessor {
  constructor() {
    this.commands = [];
  }

  process(document) {
    this.beforeProcessing(document);
    this.setupCommands();
    this.executeCommands(document);
    this.afterProcessing(document);
  }

  beforeProcessing(document) {
    console.log(`开始处理文档: ${document.name}`);
  }

  setupCommands() {
    throw new Error("必须实现setupCommands方法");
  }

  executeCommands(document) {
    this.commands.forEach(command => command.execute(document));
  }

  afterProcessing(document) {
    console.log(`文档处理完成: ${document.name}`);
  }
}

class PDFProcessor extends DocumentProcessor {
  setupCommands() {
    this.commands = [
      new ValidateCommand(),
      new ExtractTextCommand(),
      new CompressCommand()
    ];
  }
}

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步