设计模式之模板方法模式

模板方法模式的定义

模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。

moduledesign

上面对模板方法模式的定义其实已经很清晰了。完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。而每个步骤的具体实现,由子类完成。

模板方法模式的适用场景:还是使用通知服务的例子。有三种通知方式:应用服务内部通知、短信和邮件三种方式。发送通知需要如下步骤:准备发送通知的内容、发送具体通知。这两步是算法的骨架,然而根据需要,可能使用三种方式中的任意一种,涉及具体细节。

模板方法模式的结构

这里涉及到两个角色:

  • 抽象模板(Abstract Template)角色。实现了模板方法,定义了算法的骨架。

    • 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。
    • 定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
  • 具体模板(Concrete Template)角色,实现抽象类中的抽象方法,完成完整的算法。

    • 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。
    • 每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。

模板方法模式的优点:

  • 具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。
  • 子类实现算法的某些细节,有助于算法的扩展。
  • 存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。

模板方法模式的不足:

  • 每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大。

模板方法模式的实现

抽象模板角色类实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class AbstractNotify {
//模板方法
public final void send() {
String text = assembleContent();
notice(text);
}
//基本方法,已经实现
private String assembleContent() {
return "content";
}
// 通知方法的声明(由子类实现)
public abstract void notice(String text);
}

邮件通知具体模板实现:

1
2
3
4
5
6
public class MailNotify extends AbstractNotify {
@Override
public void notice(String text) {
//相关的业务
}
}

短信通知具体模板实现:

1
2
3
4
5
6
public class SMSNotify extends AbstractNotify {
@Override
public void notice(String text) {
//相关的业务
}
}

客户端进行调用短信通知策略:

1
2
3
4
5
6
7
8
public class Client {
public static void main(String[] args) {
//选择并创建需要使用的具体模板
AbstractNotify notify = new SMSNotify();
//SMS发送
notify.send();
}
}

总结

上面的实现中使用了JAVA的继承机制,在抽象类中定义一个模板方法,该方法引用了若干个抽象方法(由子类实现)或具体方法(子类可以覆盖重写)。模板方法模式是一种类的行为型模式,在它的结构图中只有类之间的继承关系,没有对象关联关系。模板方法模式是基于继承的代码复用基本技术,模板方法模式的结构和用法也是面向对象设计的核心之一。
在模板方法模式中,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中。在模板方法模式中,我们需要准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模板方法模式的用意。模板方法模式体现了面向对象的诸多重要思想,是一种使用频率较高的模式。

上面的实现其实少了一个钩子方法,它在抽象类中不做事,或者是默认的事情,子类可以选择覆盖它。
最后,为了防止子类改变模板方法中的算法骨架,一般将模板方法声明为final

vs策略方法模式

策略模式和模板方法都是用于封装算法,前者是利用组合和委托模型,而后者则是继承。从具体实现来看,策略模式与模版方法模式的区别仅仅是多了一个单独的封装类Context,它与模版方法模式的区别在于:在模版方法模式中,调用算法的主体在抽象的父类中,而在策略模式中,调用算法的主体则是封装到了封装类Context中,抽象策略Strategy一般是一个接口,目的只是为了定义规范,里面一般不包含逻辑。其实,这只是通用实现,而在实际编程中,因为各个具体策略实现类之间难免存在一些相同的逻辑,为了避免重复的代码,我们常常使用抽象类来担任Strategy的角色,在里面封装公共的代码,因此,在很多应用的场景中,在策略模式中一般会看到模版方法模式的影子。

参考

  1. 设计模式之 - 模板模式(Template Pattern)
  2. 《JAVA与模式》之模板方法模式
aoho wechat
欢迎您扫一扫上面的微信公众号,aoho求索,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!