阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 模板方法模式(Template Method)的算法骨架定义

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

作者:陈川 阅读数:12909人阅读 分类: JavaScript

模板方法模式(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() {}
}

JavaScript实现示例

下面是一个实际应用场景的完整示例,展示如何用模板方法模式处理不同类型的文档导出:

// 文档导出器抽象类
class DocumentExporter {
  export() {
    this.prepareData();
    this.formatHeader();
    this.formatContent();
    this.formatFooter();
    if (this.needCompression()) {
      this.compress();
    }
    this.save();
  }

  prepareData() {
    console.log("准备导出数据...");
  }

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

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

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

  // 钩子方法,默认不压缩
  needCompression() {
    return false;
  }

  compress() {
    console.log("压缩文档...");
  }

  save() {
    console.log("保存文档到磁盘...");
  }
}

// PDF导出实现
class PDFExporter extends DocumentExporter {
  formatHeader() {
    console.log("生成PDF文件头...");
  }

  formatContent() {
    console.log("将内容转换为PDF格式...");
  }

  formatFooter() {
    console.log("添加PDF页脚和页码...");
  }
}

// CSV导出实现
class CSVExporter extends DocumentExporter {
  formatHeader() {
    console.log("生成CSV标题行...");
  }

  formatContent() {
    console.log("将数据转换为CSV格式...");
  }

  formatFooter() {
    console.log("添加CSV结束标记...");
  }

  // 覆盖钩子方法
  needCompression() {
    return true;
  }
}

// 使用示例
const pdfExporter = new PDFExporter();
pdfExporter.export();

const csvExporter = new CSVExporter();
csvExporter.export();

模式特点与优势

模板方法模式具有以下显著特点:

  1. 封装不变部分:将算法中不变的行为移到父类,去除子类中的重复代码
  2. 扩展可变部分:通过子类实现可变部分,便于扩展新的行为
  3. 反向控制结构:父类调用子类的操作,而不是子类调用父类
  4. 符合开闭原则:增加新的子类不需要修改父类代码

钩子方法的应用

钩子方法是一种可选的操作,子类可以决定是否覆盖它。钩子通常有默认实现,让子类选择性地"挂钩"到算法的特定点:

class GameAI {
  // 模板方法
  turn() {
    this.collectResources();
    this.buildStructures();
    if (this.shouldBuildUnits()) {
      this.buildUnits();
    }
    this.attack();
  }

  collectResources() {
    // 默认实现
  }

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

  // 钩子方法
  shouldBuildUnits() {
    return true;
  }

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

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

// 具体AI实现
class MonsterAI extends GameAI {
  buildStructures() {
    console.log("怪物不建造结构");
  }

  buildUnits() {
    console.log("生成怪物单位");
  }

  attack() {
    console.log("怪物攻击玩家");
  }

  // 覆盖钩子方法
  shouldBuildUnits() {
    return Math.random() > 0.5;
  }
}

实际应用场景

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

  1. 框架生命周期:如React组件的生命周期方法
  2. 数据处理流程:如数据验证、转换、保存的标准流程
  3. UI渲染:如渲染前的准备、渲染主体、渲染后的处理
  4. 测试用例:测试前的准备、执行测试、测试后的清理

下面是一个前端表单验证的示例:

class FormValidator {
  validate(formData) {
    this.beforeValidation();
    const errors = {};
    
    for (const field of this.getFieldsToValidate()) {
      const value = formData[field];
      const fieldErrors = this.validateField(field, value);
      if (fieldErrors.length > 0) {
        errors[field] = fieldErrors;
      }
    }
    
    this.afterValidation(errors);
    return errors;
  }

  beforeValidation() {
    console.log("开始验证...");
  }

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

  validateField(field, value) {
    throw new Error("必须实现validateField方法");
  }

  afterValidation(errors) {
    console.log("验证完成,发现错误:", Object.keys(errors).length);
  }
}

class LoginFormValidator extends FormValidator {
  getFieldsToValidate() {
    return ['username', 'password'];
  }

  validateField(field, value) {
    const errors = [];
    
    if (field === 'username') {
      if (!value) errors.push("用户名不能为空");
      if (value && value.length < 6) errors.push("用户名至少6个字符");
    }
    
    if (field === 'password') {
      if (!value) errors.push("密码不能为空");
      if (value && value.length < 8) errors.push("密码至少8个字符");
    }
    
    return errors;
  }
}

// 使用示例
const validator = new LoginFormValidator();
const errors = validator.validate({
  username: 'admin',
  password: '123'
});
console.log(errors);

与策略模式的比较

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

  1. 继承 vs 组合:模板方法使用继承,策略模式使用组合
  2. 算法完整性:模板方法保持算法结构完整,策略模式完全替换算法
  3. 运行时变化:策略模式可以在运行时切换算法,模板方法在编译时确定
// 策略模式实现对比
class ValidatorStrategy {
  constructor(strategy) {
    this.strategy = strategy;
  }

  validate(formData) {
    return this.strategy.validate(formData);
  }
}

const loginStrategy = {
  validate(formData) {
    const errors = {};
    // 验证逻辑...
    return errors;
  }
};

const validator = new ValidatorStrategy(loginStrategy);
validator.validate({/*...*/});

模式变体与扩展

模板方法模式可以与其他模式结合使用,形成更强大的解决方案:

  1. 工厂方法:模板方法中的某些步骤可以使用工厂方法创建对象
  2. 观察者模式:在算法关键点触发事件通知观察者
  3. 装饰器模式:动态添加额外的行为到模板方法的某些步骤
// 结合观察者模式的示例
class ObservableDocumentExporter extends DocumentExporter {
  constructor() {
    super();
    this.observers = [];
  }

  addObserver(observer) {
    this.observers.push(observer);
  }

  notify(event) {
    this.observers.forEach(observer => observer.update(event));
  }

  export() {
    this.notify('exportStarted');
    super.export();
    this.notify('exportCompleted');
  }
}

class ExportLogger {
  update(event) {
    console.log(`导出事件: ${event}`);
  }
}

const exporter = new ObservableDocumentExporter();
exporter.addObserver(new ExportLogger());
exporter.export();

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌