模版方法很好的提现了继承的思想,我用它为核心完成了支付集成的开源项目,在使用过程中受益匪浅。

在设计模式分类中,模板方法被分在 行为型模式

在我最近的项目中我也经常使用。然后在看YII的源码时,它的身影也随处可见。忍不住想要将它说一说。希望能够帮助大家解决一些开发中灵活扩展的问题。

定义

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以在不改变一个算法的结构的情况下重定义该算法的某些特定的步骤

  • 行为型模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。
  • 行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。

上面两点是行为型模式的一个特征。以上两点在 模板方法模式 中体现的非常明显。

教科书给出的概念读起来总是这么绕口。搞得人云里雾里的。还是结合代码来说吧。

类图

image

模板方法code案例

为了让大家能够在实际中看到设计模式的使用。我就直接用YII中的一些代码片段来讲。然后大家可以下载YII下来。然后自己对照看一看。

根据上面的类图。首先需要一个抽象的父类。它里边定义了一个 templateMethod 这个方法内部会调用 primitiveOperation1 primitiveOperation2 这两个方法。但是调用的两个发放是抽象的。要在子类中来具体决定实现。

先把示例代码写出来,大家看一看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

abstract class BaseObject {

/**
* 顶层组织逻辑的方法
*/

public function method() {
$this->primitiveOperation1();
$this->primitiveOperation2();
}

/**
* 基本方法1
*/

abstract protected function primitiveOperation1();

/**
* 基本方法2
*/

abstract protected function primitiveOperation2();
}

这个抽象类定义好了调用的层次。凡是继承了它的子类,都需要实现这两个方法,并且完成自己相应的逻辑。比如一个子类继承了它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class ConcreteClass extends AbstractClass{
/**
* 基本方法1
*/

protected function primitiveOperation1() {
echo 'primitiveOperation1<br />';
}

/**
* 基本方法2
*/

protected function primitiveOperation2(){
echo 'primitiveOperation2<br />';
}

以上基本上就是模板方法模式的全部代码。不知道大家是否从中领教到它的厉害之处?我先不解释,接下来看看YII中对这种设计模式的应用

YII中的模板方法

凡是用过YII的都知道,它有一个 Object 类。这个类是整个YII的基础。今天我们不说YII。单说设计模式。就略过不说了。下面是简化后的 Object代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

class Object implements Configurable
{

public function __construct($config = [])
{

if (!empty($config)) {
Yii::configure($this, $config);
}

// 注意这个方法
$this->init();
}

// YII中为它定义了一个空实现
public function init()
{

}
}

YII在 Object 的构造函数中,调用 Object::init() 这个方法。但是这个方法是一个空实现,因此具体要init的东西,延迟到了子类进行实现,

看看YII的代码,就知道基本上所有的类都是继承自这个类,每个类的init都可以进行个性化定制。

它的实现,我们可以看看 ActiveController 中代码的实现

1
2
3
4
5
6
7
8
9
10
11

class ActiveController extends Controller
{

public function init()
{

parent::init();
if ($this->modelClass === null) {
throw new InvalidConfigException('The "modelClass" property must be set.');
}
}
}

这里 Controller 也是继承自 Object 大家可以自己追踪一下代码。

总结

模板方法模式使用的范围非常广泛。总结一下它的特点

  • 将具体的一些实现延迟到子类,可以减少父类初始化的压力
  • 父类只定义调用步骤,具体每一步的实现由子类自己决定
  • 这样的结构利用了一种反射的思路,由父类来调用子类的具体实现方法
  • 很好的进行了代码的复用

在YII中有很多模板方法的使用,大家可以自己去阅读代码看一看。这种设计模式带来的一个麻烦的地方是查看代码的不方便。
比如:经常继承一个类后实现了一些方法,但是这个方法的调用关系一头雾水,如果遇到这种情况,建议去父类看看是否有个方法组织调用了你重写的方法。