设计模式笔记(六)– 模板方法模式

模板方法模式定义了任务的模板或骨架,具体的操作由子类完成。


问题提出

假设我们要创建一个银行应用程序。

我们需要对用户的操作进行记录,每次操作时我们都要跟踪记录谁在什么时间操作了什么。这里我们举例两个不同操作:账户间转账和生成一些财务报表。

我们不想在一个类中通过不同的方法来实现这两个操作,因为如果我们要修改某个操作或想支持新的操作就要回来修改这个类。我们希望我们的系统对修改闭合而对扩展开放。所以我们对每一种操作都用一个类来表示。

我们来看一下。

我们创建TransferMoneyTask类和GenerateReportTast类,分别表示账户间转账和生成报表操作。每类操作都有一个AuditTrail对象,一个方法 execute。在execute方法里,首先调用AuditTrail的record方法用于记录操作,然后再执行相应的操作。

这个实现有一些问题。

首先,代码有重复。每次创建新操作任务时,我们都需要按这个结构添加一个类型为AuditTrail的私有属性,并用构造函数初始化,这就是重复。

第二,没有人强制我们按这个结构处理。明天有一个新人加入公司负责软件开发,他如果不按照这个结构开发,审计跟踪记录就没有了。

我们该如何使用面向对象的原则来解决这个问题?


模板方法模式

解决这个问题有两种方法。

1.利用多态性原则。

2.继承原则。

首先看多态性原则。

将共同的行为提取出来形成接口Task,定义方法execute(), 具体的操作任务如TransferMoney、GenerateReport实现Task接口的execute(),在TaskExecutor类的execute方法中,首先调用auditTrail.record,然后直接委托给具体的Task实现类执行execute。这个模式实际就是策略模式(strategy pattern)。

再看继承原则。

目前我们的两个类如下。

提取两个类的共同部分形成抽象类Task,该类的execute方法里,先调用审计跟踪record方法,然后执行实际的任务doExecute()。doExecute是抽象方法,由具体的Task实现类来处理。

这样就形成了模板方法模式,该模式定义了一个操作任务的模板或者说是骨架。

“四人帮”模板方法模式的图是这样的。

并不一定就使用抽象方法,我们也可以编写默认的实现。继承的具体操作子类可以决定重写方法还是使用默认的的实现。

我们将这些方法称为Hooks,在Java、Javascript、C#等语言以及很多框架上都能见到Hooks,非常常见。


代码实现

增加抽象类Task,它有一个AuditTrail属性,构造函数可以传递AuditTrail初始化该对象,也可以默认自动新建一个AuditTrail对象。定义一个抽象方法doExecute,访问修饰符使用protected,这样只有子类可见,外部不可见。方法execute里,先进行审计记录auditTrail.record,然后执行doExecute。

修改TransferMoneyTask类使其扩展于Task,再实现doExecute方法。然后到Main类进行调用,我们使用默认构造函数,简化了TransferMoneyTask的实现,它使用运行时生成的AuditTrail对象来审计记录。在Main中,我们是看不到doExecute方法的,只有execute,这样用户就不会漏掉AuditTrail的审计记录。

运行程序,结果是符合预期的。

同样的,修改GenerateReportTask类。GenerateReportTask我们通过构造函数传递auditTrail对象,实际场景这种方式可能会用得更多。


小结

模板方法模式定义了一个操作任务的模板或骨架,具体的操作任务由子类完成。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注