模板方法模式 gaunthan Posted on Jan 20 2017 ? Design Patterns ? > **模板方法模式**(Template Method Pattern)在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。 <!--more--> ## 概述 模板方法为我们提供了一种代码复用的重要技巧。模板方法的抽象类可以定义具体方法、抽象方法和钩子: - 抽象方法由子类实现。 - 钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要覆盖它。 为了防止子类改变模板方法中的算法,可以将模板方法声明为 `final`。对于 Java 而言可以这样,但在 C++ 中则不行。C++ 中的 final 关键字是用于阻止进一步派生和虚函数的重写的,因此将非虚函数声明为 final 将引起编译错误。 在真实世界中,模板方法模式有许多变体,如使用`sort()`算法和`comparable`接口的方式。[工厂模式](http://gaunthan.leanote.com/post/%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F)是模板方法的一种特殊版本。 ## 一个案例 模板方法模式的一个应用是测试排序算法的性能。测试排序算法的过程大致如下: 1. 读取待排序的数据集; 2. 初始化计时器; 3. 启动计时器; 4. **对该数据集调用某一特定的排序算法**; 5. 停止计时器; 6. 输出计时结果。 整个过程只有“调用特定排序算法”这一步骤是不一样的。这意味着我们需要一个设定好的框架,然后还允许我们指定这个框架中的一些操作。将这个框架抽象为一个方法,便可以达成目的。而这个方法,便是模板方法。 模板方法模式就是用来解决上面这样的问题的。它定义了一个算法的步骤,同时允许子类为一个或多个步骤提供实现。因此,按照模板方法模式设计上面的程序的话,可以导出一个抽象类,它有以下成员: - 一个(私有的)抽象方法:`sort()`。子类需要覆盖这个方法以使用特定的排序算法进行排序。 - 一些私有的成员函数,如:`inputData()`、`initTimer()`、`startTimeer()`,`endTimer()`,`outputResult()`。 - 一个公有的(final)方法:`testPerformanceOfSorting()`。该方法提供给需要测试的客户使用。 通过让子类继承这个抽象基类并实现`sort()`接口,避免了大量的重复代码,同时由于算法只存在于一个地方,因此非常容易修改。 还可用[策略模式](http://gaunthan.leanote.com/post/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F-Strategy-Pattern)来设计上面这个应用。 ## 结构 模板方法模式的类图如下所示:  显然,模板方法规定了算法的各个步骤,其中有一些公有的和私有的步骤。公有的步骤在模板方法所在类中即有定义,而某些私有的、抽象的显然应该只是声明了而已,还需待子类实现。 ## 钩子 钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。 比如上面提到的排序相关的例子,如果我们不想输出计时结果,则可以给抽象类加上一个钩子: ```cpp bool wantOutputingResult() { return true; } ``` 同时还要把钩子挂到模板方法中: ```cpp void testPerformanceOfSorting() { // ... if(wantOutputingResult()) outputResult(); // ... } ``` 为了使用钩子,只需在子类中覆盖它。 ## References - 弗里曼弗里曼谢拉贝茨 O'ReillyTaiwan 公司 UMLChina. Head First 设计模式 [M]. 中国电力出版社, 2007. 赏 Wechat Pay Alipay 数据结构:链表 外观模式