模板方法模式(Template Method)的算法骨架定义
模板方法模式(Template Method)的算法骨架定义
模板方法模式是一种行为设计模式,它在父类中定义了一个算法的骨架,允许子类在不改变算法结构的情况下重写某些步骤。这种模式的核心在于将不变的部分封装在父类,可变的部分交给子类实现。
模式结构与角色
模板方法模式通常包含以下角色:
- 抽象类(Abstract Class):定义算法骨架和基本操作
- 具体类(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();
模式特点与优势
模板方法模式具有以下显著特点:
- 封装不变部分:将算法中不变的行为移到父类,去除子类中的重复代码
- 扩展可变部分:通过子类实现可变部分,便于扩展新的行为
- 反向控制结构:父类调用子类的操作,而不是子类调用父类
- 符合开闭原则:增加新的子类不需要修改父类代码
钩子方法的应用
钩子方法是一种可选的操作,子类可以决定是否覆盖它。钩子通常有默认实现,让子类选择性地"挂钩"到算法的特定点:
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;
}
}
实际应用场景
模板方法模式在前端开发中有广泛的应用:
- 框架生命周期:如React组件的生命周期方法
- 数据处理流程:如数据验证、转换、保存的标准流程
- UI渲染:如渲染前的准备、渲染主体、渲染后的处理
- 测试用例:测试前的准备、执行测试、测试后的清理
下面是一个前端表单验证的示例:
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);
与策略模式的比较
模板方法模式和策略模式都用于封装算法,但有以下区别:
- 继承 vs 组合:模板方法使用继承,策略模式使用组合
- 算法完整性:模板方法保持算法结构完整,策略模式完全替换算法
- 运行时变化:策略模式可以在运行时切换算法,模板方法在编译时确定
// 策略模式实现对比
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({/*...*/});
模式变体与扩展
模板方法模式可以与其他模式结合使用,形成更强大的解决方案:
- 工厂方法:模板方法中的某些步骤可以使用工厂方法创建对象
- 观察者模式:在算法关键点触发事件通知观察者
- 装饰器模式:动态添加额外的行为到模板方法的某些步骤
// 结合观察者模式的示例
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