模板方法模式的定义
模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。
上面对模板方法模式的定义其实已经很清晰了。完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。而每个步骤的具体实现,由子类完成。
模板方法模式的适用场景:还是使用通知服务的例子。有三种通知方式:应用服务内部通知、短信和邮件三种方式。发送通知需要如下步骤:准备发送通知的内容、发送具体通知。这两步是算法的骨架,然而根据需要,可能使用三种方式中的任意一种,涉及具体细节。
模板方法模式的结构
这里涉及到两个角色:
模板方法模式的优点:
- 具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。
- 子类实现算法的某些细节,有助于算法的扩展。
- 存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。
模板方法模式的不足:
- 每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大。
模板方法模式的实现
抽象模板角色类实现:
public abstract class AbstractNotify {
public final void send() {
String text = assembleContent();
notice(text);
}
private String assembleContent() {
return "content";
}
public abstract void notice(String text);
}
邮件通知具体模板实现:
public class MailNotify extends AbstractNotify {
@Override
public void notice(String text) {
}
}
短信通知具体模板实现:
public class SMSNotify extends AbstractNotify {
@Override
public void notice(String text) {
}
}
客户端进行调用短信通知策略:
public class Client {
public static void main(String[] args) {
AbstractNotify notify = new SMSNotify();
notify.send();
}
}
总结
上面的实现中使用了JAVA的继承机制,在抽象类中定义一个模板方法,该方法引用了若干个抽象方法(由子类实现)或具体方法(子类可以覆盖重写)。模板方法模式是一种类的行为型模式,在它的结构图中只有类之间的继承关系,没有对象关联关系。模板方法模式是基于继承的代码复用基本技术,模板方法模式的结构和用法也是面向对象设计的核心之一。
在模板方法模式中,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中。在模板方法模式中,我们需要准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模板方法模式的用意。模板方法模式体现了面向对象的诸多重要思想,是一种使用频率较高的模式。
上面的实现其实少了一个钩子方法,它在抽象类中不做事,或者是默认的事情,子类可以选择覆盖它。
最后,为了防止子类改变模板方法中的算法骨架,一般将模板方法声明为final
vs策略方法模式
策略模式和模板方法都是用于封装算法,前者是利用组合和委托模型,而后者则是继承。从具体实现来看,策略模式与模版方法模式的区别仅仅是多了一个单独的封装类Context,它与模版方法模式的区别在于:在模版方法模式中,调用算法的主体在抽象的父类中,而在策略模式中,调用算法的主体则是封装到了封装类Context中,抽象策略Strategy一般是一个接口,目的只是为了定义规范,里面一般不包含逻辑。其实,这只是通用实现,而在实际编程中,因为各个具体策略实现类之间难免存在一些相同的逻辑,为了避免重复的代码,我们常常使用抽象类来担任Strategy的角色,在里面封装公共的代码,因此,在很多应用的场景中,在策略模式中一般会看到模版方法模式的影子。
订阅最新文章,欢迎关注我的公众号
参考
- 设计模式之 - 模板模式(Template Pattern)
- 《JAVA与模式》之模板方法模式