模板方法模式 - Template Pattern
定义
定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
设计的原则和思想
- 解耦的是 模板方法(算法)和方法(算法)步骤。
- 不变部分是方法步骤,变化部分是模板方法。
- 核心思想是同一个步骤,不同的事情。
一句话概括设计模式
父类控制流程,子类负责具体实现。
结构中包含的角色
- AbstractClass(抽象类)
- ConcreteClass(具体子类)
最小可表达代码
abstract class AbstractTemplate
{
public function templateMethod()
{
$this->primitiveOperation1();
$this->primitiveOperation2();
if ($this->primitiveOperation3()) {
echo '执行钩子';
}
}
// 具体方法
public function primitiveOperation1()
{
echo '我是具体方法';
}
// 抽象方法
public abstract function primitiveOperation2();
// 钩子方法
public function primitiveOperation3() : bool{
return false;
}
}
class ConcreteTemplate extends AbstractTemplate
{
public function primitiveOperation2()
{
echo '我是实现的抽象方法';
}
public function primitiveOperation3() : bool{
return true;
}
}
$template = new ConcreteTemplate();
$template->templateMethod();
优点
- 可实现反向控制结构,通过子类覆盖父类的钩子方法来决定某一特定步骤是否需要执行。
- 封装不变部分,扩展可变部分。
- 提取公共代码,便于维护。
- 行为由父类控制,子类实现。
缺点
- 每一个基本方法的不同实现都需要一个子类来实现,如果父类中可变的基本方法太多,将会导致类的个数增加。
- 部分客户端可能会受到算法框架的限制。
何时使用
- 流程不变,流程内的实现可以被替换的时候。
- 多个算法几乎一样时。
- 只希望扩展某个特定算法步骤,而不是整个算法或其结构时。
- 有多个子类共有的方法,且逻辑相同。
实际应用场景
- 建房子,地基、走线、水管都一样,但是装修是不一样的。
- 报表。报表头,报表内容,报表结尾,这些步骤都是一样的。
- 考试。不同的科目,考试流程是一样的,但是考试内容是不一样的。
- 面试。面试流程都是一样的,不一样的是面试内容。
- 同步回调。
模板模式的作用
复用
子类可以复用父类模板方法的代码。
扩展
用户可以在不修改框架源码的情况下,扩展定制化框架的功能。
模板方法模式 VS 回调(Callback)
- 同步回调跟模板模式几乎一致。它们都是在一个大的算法骨架中,自由替换其中的某个步骤,起到代码复用和扩展的目的。
- 回调基于组合关系来实现,把一个对象传递给另一个对象,是一种对象之间的关系。模板模式基于继承关系来实现,是一种类之间的关系。
- 回调可以使用匿名类来创建回调对象,可以不用事先定义类。而模板模式针对不同的实现都要定义不同的子类。
- 模板方法中,子类必须实现所有的抽象方法。而回调只需要往用到的模板方法中注入回调对象即可。
回调代码
class Template
{
public function templateMethod(BaseCallback $callback)
{
// 业务处理1
$callback->callbackMethod();
// 业务处理2
}
}
interface BaseCallback
{
public function callbackMethod();
}
class SubCallback implements BaseCallback
{
public function callbackMethod()
{
echo "SubCallback callbackMethod";
}
}
$template = new Template();
$subCallback = new SubCallback();
$template->templateMethod($subCallback);