模板方法模式
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模式结构
抽象模板(AbstractClass)角色: 定义一个或多个抽象方法让子类实现。这些抽象方法叫做基本操作,它们是顶级逻辑的组成部分。
定义一个模板方法。这个模板方法一般是一个具体方法,它给出顶级逻辑的骨架,而逻辑的组成步骤在对应的抽象操作中,这些操作将会推迟到子类中实现。同时,顶层逻辑也可以调用具体的实现方法。
具体模板(ConcrteClass)角色:实现父类的一个或多个抽象方法,作为顶层逻辑的组成而存在。
每个抽象模板可以有多个具体模板与之对应,而每个具体模板有其自己对抽象方法(也就是顶层逻辑的组成部分)的实现,从而使得顶层逻辑的实现各不相同。
结构图
PHP代码实现
<?php
// 抽象类定义了一个操作中的算法骨架(模板方法)
abstract class AbstractClass {
// 模板方法定义了算法的框架
final public function templateMethod(): void {
$this->baseOperation1();
$this->requiredOperation1();
$this->baseOperation2();
$this->hook1();
$this->requiredOperation2();
$this->baseOperation3();
$this->hook2();
}
// 这些操作已在这个类中实现
protected function baseOperation1(): void {
echo "AbstractClass says: I am doing the bulk of the work\n";
}
protected function baseOperation2(): void {
echo "AbstractClass says: But I let subclasses override some operations\n";
}
protected function baseOperation3(): void {
echo "AbstractClass says: But I am doing the bulk of the work anyway\n";
}
// 这些操作必须在子类中实现
abstract protected function requiredOperation1(): void;
abstract protected function requiredOperation2(): void;
// 这些是"钩子"操作,子类可以覆盖它们,但不是必须的,因为钩子提供了默认的行为
protected function hook1(): void { }
protected function hook2(): void { }
}
// 具体子类可以覆盖父类方法的实现,但不会改变算法的框架
class ConcreteClass1 extends AbstractClass {
protected function requiredOperation1(): void {
echo "ConcreteClass1 says: Implemented Operation1\n";
}
protected function requiredOperation2(): void {
echo "ConcreteClass1 says: Implemented Operation2\n";
}
}
class ConcreteClass2 extends AbstractClass {
protected function requiredOperation1(): void {
echo "ConcreteClass2 says: Implemented Operation1\n";
}
protected function requiredOperation2(): void {
echo "ConcreteClass2 says: Implemented Operation2\n";
}
protected function hook1(): void {
echo "ConcreteClass2 says: Overridden Hook1\n";
}
}
// 客户端代码
function clientCode(AbstractClass $class) {
// ...
$class->templateMethod();
// ...
}
echo "Same client code can work with different subclasses:\n";
clientCode(new ConcreteClass1());
echo "\n";
echo "Same client code can work with different subclasses:\n";
clientCode(new ConcreteClass2());