在面向对象开发过程中,通常我们会遇到这样的一个问题:我们知道一个算法所需的关键步骤,并确定了这些步骤的执行顺序,但是某些更细节步骤的具体实现是未知的,或者说某些步骤的实现与具体的环境相关。
在这种情况下,在一个方法中定义一个的算法的骨架,而将一些步骤的实现延迟到子类中,我们称这个方法为:模版方法。
模式结构
抽象类(AbstractClass):定义抽象的原始操作(primitive operation),具体的子类将重定义它们以实现一个算法;实现一个模板方法,在其中定义一个算法的骨架,算法中会去调用原始操作。
具体子类(ConcreteClass):实现原始操作以完成算法中与特定子类相关的步骤。
模式分析
1)是一种类的行为型模式,在它的结构图中只有类之间的继承关系,没有对象关联关系;
2)基于继承的代码复用基本技术,模板方法模式的结构和用法也是面向对象设计的核心之一;
3)其关键点是将通用算法封装在抽象基类中,并将不同的算法细节放到子类中实现。
模式优点
1)在一个类中形式化地定义算法,而由它的子类实现细节的处理;
2)是一种代码复用的基本技术。它们提取了类库中的公共行为;
3)形成一种反向的控制结构,通过对子类的扩展增加新的行为,符合“开放封闭原则”。
模式缺点
继承的强制性约束关系也让模版方法模式有不足的地方,我们可以看到对于各个 ConcreteClass 类中的实现的原始方法 Primitive() 是不能被别的类复用的。假设我们要创建一个 AbstractClass 的变体 AnotherAbstractClass,并且两者只是通用算法不一样,其原始操作想复用 AbstractClass 的子类的实现。但是这是不可能实现的,因为 ConcreteClass 继承自 AbstractClass,也就继承了 AbstractClass 的通用算法,AnotherAbstractClass 是复用不了 ConcreteClass 的实现,因为后者不是继承自前者。
适用环境
1)一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2)各子类中公共的行为被提取出来并集中到一个公共父类中以避免代码重复。
3)控制子类扩展。模板方法只在特定点调用原始操作 ,这样就只允许在这些点进行扩展。
相关模式
策略模式:策略模式解决的是和模版方法模式类似的问题,但是策略模式是将逻辑(算法)封装到一个类中,并采取组合(委托)的方式解决这个问题。
实际应用
Zendframework 1.* 中的 Controller 就是用模版方法模式实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
class Zend_Controller_Action { private $request; public function __construct($request) { $this->request = $request; //一些初始化工作 //... $this->init(); } public function dispatch() { $this->preDispatch(); //... //从 $this->request->getActionName() 得到请求的 action 名字,如:list、detail $action = $this->request->getActionName() . 'Action'; $this->$action(); //执行请求的 action //... $this->postDispatch(); } public function init() { } public function preDispatch() { } public function postDispatch() { } } class BlogController extends Zend_Controller_Action { public function init() { //这里可以做一些该 controller 要初始化的工作 //比如:设置博客界面的布局(setLayout) //... } public function preDispatch() { //这里可以做一些 action 执行前需要做的工作 //比如:设置网页标题为 “xxx的博客” //... } public function postDispatch() { //这里可以做一些 action 执行后需要做的工作 //... } public function listAction() { //实现 blog 文章列表 //... } public function detailAction() { //实现 blog 单篇文章展示 //... } } $controller = new BlogController($request); $controller->dispatch(); |
父类中的构造函数、dispatch() 都是模版方法,而 init()、preDispatch()、postDispatch() 这些钩子方法就是供子类扩展的。
参考:
wiki/Template_method_pattern
http://blog.csdn.net/hguisu/article/details/7564039
http://www.cnblogs.com/phinecos/archive/2006/10/10/524745.html