# 模板方法模式深度解析(三)
4 钩子方法的使用
模板方法模式中,在父类中提供了一个定义算法框架的模板方法,还提供了一系列抽象方法、具体方法和钩子方法,其中钩子方法的引入使得子类可以控制父类的行为。最简单的钩子方法就是空方法,代码如下:
```
public virtual void Display() { }
```
当然也可以在钩子方法中定义一个默认的实现,如果子类不覆盖钩子方法,则执行父类的默认实现代码。
另一种钩子方法可以实现对其他方法进行约束,这种钩子方法通常返回一个bool类型,即返回true或false,用来判断是否执行某一个基本方法,下面通过一个实例来说明这种钩子方法的使用。
某软件公司欲为销售管理系统提供一个数据图表显示功能,该功能的实现包括如下几个步骤:
(1) 从数据源获取数据;
(2) 将数据转换为XML格式;
(3) 以某种图表方式显示XML格式的数据。
该功能支持多种数据源和多种图表显示方式,但所有的图表显示操作都基于XML格式的数据,因此可能需要对数据进行转换,如果从数据源获取的数据已经是XML数据则无须转换。
由于该数据图表显示功能的三个步骤次序是固定的,且存在公共代码(例如数据格式转换代码),满足模板方法模式的适用条件,可以使用模板方法模式对其进行设计。因为数据格式的不同,XML数据可以直接显示,而其他格式的数据需要进行转换,因此第(2)步“将数据转换为XML格式”的执行存在不确定性,为了解决这个问题,可以定义一个钩子方法IsNotXMLData()来对数据转换方法进行控制。通过分析,该图表显示功能的基本结构如图4所示:
![](http://img.my.csdn.net/uploads/201212/15/1355579556_7922.jpg)
图4 数据图表显示功能结构图
可以将公共方法和框架代码放在抽象父类中,代码如下:
```
//DataViewer.cs
using System;
namespace TemplateMethodSample
{
abstract class DataViewer
{
//抽象方法:获取数据
public abstract void GetData();
//具体方法:转换数据
public void ConvertData()
{
Console.WriteLine("将数据转换为XML格式。");
}
//抽象方法:显示数据
public abstract void DisplayData();
//钩子方法:判断是否为XML格式的数据
public virtual bool IsNotXMLData()
{
return true;
}
//模板方法
public void Process()
{
GetData();
//如果不是XML格式的数据则进行数据转换
if (IsNotXMLData())
{
ConvertData();
}
DisplayData();
}
}
}
```
在上面的代码中,引入了一个钩子方法IsNotXMLData(),其返回类型为bool类型,在模板方法中通过它来对数据转换方法ConvertData()进行约束,该钩子方法的默认返回值为true,在子类中可以根据实际情况覆盖该方法,其中用于显示XML格式数据的具体子类XMLDataViewer代码如下:
```
//XMLDataViewer.cs
using System;
namespace TemplateMethodSample
{
class XMLDataViewer : DataViewer
{
//实现父类方法:获取数据
public override void GetData()
{
Console.WriteLine("从XML文件中获取数据。");
}
//实现父类方法:显示数据,默认以柱状图方式显示,可结合桥接模式来改进
public override void DisplayData()
{
Console.WriteLine("以柱状图显示数据。");
}
//覆盖父类的钩子方法
public override bool IsNotXMLData()
{
return false;
}
}
}
```
在具体子类XMLDataViewer中覆盖了钩子方法IsNotXMLData(),返回false,表示该数据已为XML格式,无须执行数据转换方法ConvertData(),客户端代码如下:
```
//Program.cs
using System;
namespace TemplateMethodSample
{
class Program
{
static void Main(string[] args)
{
DataViewer dv;
dv = new XMLDataViewer();
dv.Process();
Console.Read();
}
}
}
```
该程序运行结果如下:
```
从XML文件中获取数据。
以柱状图显示数据。
```
5 模板方法模式效果与适用场景
模板方法模式是基于继承的代码复用技术,它体现了面向对象的诸多重要思想,是一种使用较为频繁的模式。模板方法模式广泛应用于框架设计中,以确保通过父类来控制处理流程的逻辑顺序(如框架的初始化,测试流程的设置等)。
5.1 模式优点
模板方法模式的主要优点如下:
(1) 在父类中形式化地定义一个算法,而由它的子类来实现细节的处理,在子类实现详细的处理算法时并不会改变算法中步骤的执行次序。
(2) 模板方法模式是一种代码复用技术,它在类库设计中尤为重要,它提取了类库中的公共行为,将公共行为放在父类中,而通过其子类来实现不同的行为,它鼓励我们恰当使用继承来实现代码复用。
(3) 可实现一种反向控制结构,通过子类覆盖父类的钩子方法来决定某一特定步骤是否需要执行。
(4) 在模板方法模式中可以通过子类来覆盖父类的基本方法,不同的子类可以提供基本方法的不同实现,更换和增加新的子类很方便,符合单一职责原则和开闭原则。
5.2 模式缺点
模板方法模式的主要缺点如下:
需要为每一个基本方法的不同实现提供一个子类,如果父类中可变的基本方法太多,将会导致类的个数增加,系统更加庞大,设计也更加抽象,此时,可结合桥接模式来进行设计。
5.3 模式适用场景
在以下情况下可以考虑使用模板方法模式:
(1) 对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些可以改变的细节由其子类来实现。即:一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。
(2) 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
(3) 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。
- Introduction
- 基础知识
- 设计模式概述
- 从招式与内功谈起——设计模式概述(一)
- 从招式与内功谈起——设计模式概述(二)
- 从招式与内功谈起——设计模式概述(三)
- 面向对象设计原则
- 面向对象设计原则之单一职责原则
- 面向对象设计原则之开闭原则
- 面向对象设计原则之里氏代换原则
- 面向对象设计原则之依赖倒转原则
- 面向对象设计原则之接口隔离原则
- 面向对象设计原则之合成复用原则
- 面向对象设计原则之迪米特法则
- 六个创建型模式
- 简单工厂模式-Simple Factory Pattern
- 工厂三兄弟之简单工厂模式(一)
- 工厂三兄弟之简单工厂模式(二)
- 工厂三兄弟之简单工厂模式(三)
- 工厂三兄弟之简单工厂模式(四)
- 工厂方法模式-Factory Method Pattern
- 工厂三兄弟之工厂方法模式(一)
- 工厂三兄弟之工厂方法模式(二)
- 工厂三兄弟之工厂方法模式(三)
- 工厂三兄弟之工厂方法模式(四)
- 抽象工厂模式-Abstract Factory Pattern
- 工厂三兄弟之抽象工厂模式(一)
- 工厂三兄弟之抽象工厂模式(二)
- 工厂三兄弟之抽象工厂模式(三)
- 工厂三兄弟之抽象工厂模式(四)
- 工厂三兄弟之抽象工厂模式(五)
- 单例模式-Singleton Pattern
- 确保对象的唯一性——单例模式 (一)
- 确保对象的唯一性——单例模式 (二)
- 确保对象的唯一性——单例模式 (三)
- 确保对象的唯一性——单例模式 (四)
- 确保对象的唯一性——单例模式 (五)
- 原型模式-Prototype Pattern
- 对象的克隆——原型模式(一)
- 对象的克隆——原型模式(二)
- 对象的克隆——原型模式(三)
- 对象的克隆——原型模式(四)
- 建造者模式-Builder Pattern
- 复杂对象的组装与创建——建造者模式(一)
- 复杂对象的组装与创建——建造者模式(二)
- 复杂对象的组装与创建——建造者模式(三)
- 七个结构型模式
- 适配器模式-Adapter Pattern
- 不兼容结构的协调——适配器模式(一)
- 不兼容结构的协调——适配器模式(二)
- 不兼容结构的协调——适配器模式(三)
- 不兼容结构的协调——适配器模式(四)
- 桥接模式-Bridge Pattern
- 处理多维度变化——桥接模式(一)
- 处理多维度变化——桥接模式(二)
- 处理多维度变化——桥接模式(三)
- 处理多维度变化——桥接模式(四)
- 组合模式-Composite Pattern
- 树形结构的处理——组合模式(一)
- 树形结构的处理——组合模式(二)
- 树形结构的处理——组合模式(三)
- 树形结构的处理——组合模式(四)
- 树形结构的处理——组合模式(五)
- 装饰模式-Decorator Pattern
- 扩展系统功能——装饰模式(一)
- 扩展系统功能——装饰模式(二)
- 扩展系统功能——装饰模式(三)
- 扩展系统功能——装饰模式(四)
- 外观模式-Facade Pattern
- 深入浅出外观模式(一)
- 深入浅出外观模式(二)
- 深入浅出外观模式(三)
- 享元模式-Flyweight Pattern
- 实现对象的复用——享元模式(一)
- 实现对象的复用——享元模式(二)
- 实现对象的复用——享元模式(三)
- 实现对象的复用——享元模式(四)
- 实现对象的复用——享元模式(五)
- 代理模式-Proxy Pattern
- 设计模式之代理模式(一)
- 设计模式之代理模式(二)
- 设计模式之代理模式(三)
- 设计模式之代理模式(四)
- 十一个行为型模式
- 职责链模式-Chain of Responsibility Pattern
- 请求的链式处理——职责链模式(一)
- 请求的链式处理——职责链模式(二)
- 请求的链式处理——职责链模式(三)
- 请求的链式处理——职责链模式(四)
- 命令模式-Command Pattern
- 请求发送者与接收者解耦——命令模式(一)
- 请求发送者与接收者解耦——命令模式(二)
- 请求发送者与接收者解耦——命令模式(三)
- 请求发送者与接收者解耦——命令模式(四)
- 请求发送者与接收者解耦——命令模式(五)
- 请求发送者与接收者解耦——命令模式(六)
- 解释器模式-Interpreter Pattern
- 自定义语言的实现——解释器模式(一)
- 自定义语言的实现——解释器模式(二)
- 自定义语言的实现——解释器模式(三)
- 自定义语言的实现——解释器模式(四)
- 自定义语言的实现——解释器模式(五)
- 自定义语言的实现——解释器模式(六)
- 迭代器模式-Iterator Pattern
- 遍历聚合对象中的元素——迭代器模式(一)
- 遍历聚合对象中的元素——迭代器模式(二)
- 遍历聚合对象中的元素——迭代器模式(三)
- 遍历聚合对象中的元素——迭代器模式(四)
- 遍历聚合对象中的元素——迭代器模式(五)
- 遍历聚合对象中的元素——迭代器模式(六)
- 中介者模式-Mediator Pattern
- 协调多个对象之间的交互——中介者模式(一)
- 协调多个对象之间的交互——中介者模式(二)
- 协调多个对象之间的交互——中介者模式(三)
- 协调多个对象之间的交互——中介者模式(四)
- 协调多个对象之间的交互——中介者模式(五)
- 备忘录模式-Memento Pattern
- 撤销功能的实现——备忘录模式(一)
- 撤销功能的实现——备忘录模式(二)
- 撤销功能的实现——备忘录模式(三)
- 撤销功能的实现——备忘录模式(四)
- 撤销功能的实现——备忘录模式(五)
- 观察者模式-Observer Pattern
- 对象间的联动——观察者模式(一)
- 对象间的联动——观察者模式(二)
- 对象间的联动——观察者模式(三)
- 对象间的联动——观察者模式(四)
- 对象间的联动——观察者模式(五)
- 对象间的联动——观察者模式(六)
- 状态模式-State Pattern
- 处理对象的多种状态及其相互转换——状态模式(一)
- 处理对象的多种状态及其相互转换——状态模式(二)
- 处理对象的多种状态及其相互转换——状态模式(三)
- 处理对象的多种状态及其相互转换——状态模式(四)
- 处理对象的多种状态及其相互转换——状态模式(五)
- 处理对象的多种状态及其相互转换——状态模式(六)
- 策略模式-Strategy Pattern
- 算法的封装与切换——策略模式(一)
- 算法的封装与切换——策略模式(二)
- 算法的封装与切换——策略模式(三)
- 算法的封装与切换——策略模式(四)
- 模板方法模式-Template Method Pattern
- 模板方法模式深度解析(一)
- 模板方法模式深度解析(二)
- 模板方法模式深度解析(三)
- 访问者模式-Visitor Pattern
- 操作复杂对象结构——访问者模式(一)
- 操作复杂对象结构——访问者模式(二)
- 操作复杂对象结构——访问者模式(三)
- 操作复杂对象结构——访问者模式(四)
- 设计模式趣味学习(复习)
- 设计模式与足球(一)
- 设计模式与足球(二)
- 设计模式与足球(三)
- 设计模式与足球(四)
- 设计模式综合应用实例
- 多人联机射击游戏
- 多人联机射击游戏中的设计模式应用(一)
- 多人联机射击游戏中的设计模式应用(二)
- 数据库同步系统
- 设计模式综合实例分析之数据库同步系统(一)
- 设计模式综合实例分析之数据库同步系统(二)
- 设计模式综合实例分析之数据库同步系统(三)