# 模板方法模式
TIP
模板方法模式是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。
# 结构
- 抽象类 (
AbstractClass
) 会声明作为算法步骤的方法, 以及依次调用它们的实际模板方法。 算法步骤可以被声明为 抽象类型, 也可以提供一些默认实现。 - 具体类 (
ConcreteClass
) 可以重写所有步骤, 但不能重写模板方法自身。
# 适用场景
当你只希望客户端扩展某个特定算法步骤, 而不是整个算法或其结构时, 可使用模板方法模式。
- 模板方法将整个算法转换为一系列独立的步骤, 以便子类能对其进行扩展, 同时还可让超类中所定义的结构保持完整。
当多个类的算法除一些细微不同之外几乎完全一样时, 你可使用该模式。 但其后果就是, 只要算法发生变化, 你就可能需要修改所有的类。
1.在将算法转换为模板方法时, 你可将相似的实现步骤提取到超类中以去除重复代码。 子类间各不同的代码可继续保留在子类中。
# 优缺点
# 优点
- 你可仅允许客户端重写一个大型算法中的特定部分, 使得算法其他部分修改对其所造成的影响减小。
- 你可将重复代码提取到一个超类中。
# 缺点
- 部分客户端可能会受到算法框架的限制。
- 通过子类抑制默认步骤实现可能会导致违反里氏替换原则。
- 模板方法中的步骤越多, 其维护工作就可能会越困难。
# 与其他模式的关系
- 工厂方法模式是模板方法模式的一种特殊形式。 同时, 工厂方法可以作为一个大型模板方法中的一个步骤。
- 模板方法基于继承机制: 它允许你通过扩展子类中的部分内容来改变部分算法。 策略模式基于组合机制: 你可以通过对相应行为提供不同的策略来改变对象的部分行为。 模板方法在类层次上运作, 因此它是静态的。 策略在对象层次上运作, 因此允许在运行时切换行为。
# 代码实现
abstract class AbstractClass {
public templateMethod(): string[] {
this.baseOperation1();
this.requiredOperations1();
this.baseOperation2();
this.hook1();
this.requiredOperation2();
this.baseOperation3();
this.hook2();
return [this.baseOperation1(), this.baseOperation2(), this.baseOperation3()];
}
protected baseOperation1(): string {
return 'baseOperation1';
}
protected baseOperation2(): string {
return 'baseOperation2';
}
protected baseOperation3(): string {
return 'baseOperation3';
}
protected abstract requiredOperations1(): void;
protected abstract requiredOperation2(): void;
protected hook1(): void {}
protected hook2(): void {}
}
class ConcreteClass1 extends AbstractClass {
protected requiredOperations1(): string {
return 'ConcreteClass1: Operation1';
}
protected requiredOperation2(): string {
return 'ConcreteClass1: Operation2';
}
}
class ConcreteClass2 extends AbstractClass {
protected requiredOperations1(): string {
return 'ConcreteClass2: Operation1';
}
protected requiredOperation2(): string {
return 'ConcreteClass2: Operation2';
}
protected hook1(): string {
return 'ConcreteClass2 Hook1';
}
}
export { AbstractClass, ConcreteClass1, ConcreteClass2 };
# 测试用例
import { ConcreteClass1 } from '../index';
describe('template pattern', () => {
it('template concrete creator', () => {
const concrete1 = new ConcreteClass1();
expect(concrete1.templateMethod()).toStrictEqual([
'baseOperation1',
'baseOperation2',
'baseOperation3',
]);
});
});