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

  模板方法模式是基于继承的代码复用的基本技术。

1.  结构

  模板方法模式的静态结构如下:

涉及到的角色如下:

抽象模板角色(AbstractClass):其责任主要如下

(1)定义了一个或多个抽象操作,以便让子类实现。这些操作叫做基本操作,它们是一个顶级逻辑的组成步骤。

(2)定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。

具体模板(ConcretrClass)角色:其责任如下

(1)实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。

(2)每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法的不同实现,从而使得顶级逻辑的实现各不相同。

代码如下:

package cn.qlq.teplate;

public abstract class AbstractClass {

    public void templateMethod() {
// 处理前 doOperation1(); doOperation2(); doOperation3(); // 处理后
} void doOperation1() {
System.out.println("doOperation1默认实现");
} abstract void doOperation2(); abstract void doOperation3(); }

  doOperation1(),doOperation2(),doOperation3()等基本方法是顶级逻辑的组成步骤。这个顶级逻辑由templateMethod方法代表。抽象类提供了doOperation1的默认实现,doOperation2和doOperation3交给子类去实现。

package cn.qlq.teplate;

public class ConcreteClass1 extends AbstractClass {

    @Override
void doOperation2() {
System.out.println("ConcreteClass1 doOperation2");
} @Override
void doOperation3() {
System.out.println("ConcreteClass1 doOperation3");
} }
package cn.qlq.teplate;

public class ConcreteClass2 extends AbstractClass {

    @Override
void doOperation1() {
System.out.println("ConcreteClass2 doOperation1");
} @Override
void doOperation2() {
System.out.println("ConcreteClass2 doOperation2");
} @Override
void doOperation3() {
System.out.println("ConcreteClass2 doOperation3");
} }

客户端代码:

package cn.qlq.teplate;

public class Client {

    public static void main(String[] args) {
AbstractClass abstractClass = new ConcreteClass2();
abstractClass.templateMethod();
} }

结果:

ConcreteClass2 doOperation1
ConcreteClass2 doOperation2
ConcreteClass2 doOperation3

2.  例子

  一个银行计息的例子。假设银行需要两种存款账号,货币市场账号(MoneyMarket)和定期(Certificate of Deposite)存款账号。这个系统的总行为是计算出利息,这也就决定了作为模板方法的顶级逻辑应当是利息计算。利息计算涉及两步:一是确定账号类型、二是确定利息的百分比。

结构图如下:

  模板方法模式的实现方法是从上向下的,也就是说需要先给出顶级的逻辑,然后给出具体步骤的逻辑。

代码如下:

package cn.qlq.template;

public abstract class Account {

    protected String accountNumber;

    public Account(String accountNumber) {
super();
this.accountNumber = accountNumber;
} public final double calculateInterest() {
double interestRate = doCalculateInterestRate();
String accountType = doCalculateAccountType();
double amount = doCalculateAmount(accountType, accountNumber); return amount * interestRate;
} abstract double doCalculateAmount(String accountType, String accountNumber2); abstract String doCalculateAccountType(); abstract double doCalculateInterestRate(); }
package cn.qlq.template;

public class CDAccount extends Account {

    public CDAccount(String accountNumber) {
super(accountNumber);
} @Override
double doCalculateAmount(String accountType, String accountNumber2) {
// 模拟从数据库查出数据
return 50D;
} @Override
String doCalculateAccountType() {
return "CDAccount";
} @Override
double doCalculateInterestRate() {
return 0.048D;
} }
package cn.qlq.template;

public class MoneyMarket extends Account {

    public MoneyMarket(String accountNumber) {
super(accountNumber);
} @Override
double doCalculateAmount(String accountType, String accountNumber2) {
// 模拟从数据库查出数据
return 50D;
} @Override
String doCalculateAccountType() {
return "Money Market";
} @Override
double doCalculateInterestRate() {
return 0.045D;
} }

客户端代码:

package cn.qlq.template;

public class Client {
public static void main(String[] args) {
Account account = new CDAccount("123");
double calculateInterest = account.calculateInterest();
System.out.println(calculateInterest); Account moneyMarket = new MoneyMarket("123");
double moneyMarketInterest = moneyMarket.calculateInterest();
System.out.println(moneyMarketInterest);
}
}

3.小结

1.继承作为复用的工具

(1)首先,初学Java的不知道什么是继承,或者认为继承是高深的工具。这样设计大部分的功能是通过委派进行的。

(2)慢慢的发现继承并不难,并且初步认识到继承可以使子类一下子得到父类的行为。于是试图将继承作为功能复用的主要工具,并把原来应当使用委派的地方改为使用继承,这时候就造成继承的滥用。

(3)设计中也提倡使用委派关系代替继承。比如状态模式、策略模式、装饰模式、桥梁模式等。

(4)事实上封装、继承、多态和抽象化并称为面向对象的特性。所以应当合理的使用继承。

2. Java语言的模板方法模式

HttpServlet技术就使用了模板方法模式。HttpServlet提供了一个service()方法,这个方法调用7个do方法中的一个或几个,完成对客户端调用的处理。

3.模板方法模式中的方法

  模板方法中的方法可以分为两类:模板方法和基本方法。

模板方法:

  一般是定义在抽象类中,把基本方法组合在一起形成一个总的方法或者行为的方法。这个方法一般定义在抽象类中并且由子类不加以修改地完全继承下来。

  一个抽象类可以有任意多个模板方法,而不限于一个。每一个模板方法都可以调用任意多个具体方法。

基本方法:

  基本方法又分为三种:抽象方法、具体方法和钩子方法

(1)抽象方法:由抽象类声明并由具体子类实现

(2)具体方法:由抽象类声明并实现,子类不实现或置换。有些具体方法可以起到工厂方法的作用,这样的具体方法又叫做工厂方法。

(3)钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。通常抽象类给出的实现是一个空实现,作为方法的默认实现(在缺省适配器模式也见过)。

注意命名规则:

  钩子方法的名字应当以do开始,这是熟悉设计模式的Java程序设计师的标准做法。在HttpServlet中也遵循这一规则。

总结:

意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

主要解决:一些方法通用,却在每一个子类都重新写了这一方法。

何时使用:有一些通用的方法。

如何解决:将这些通用算法抽象出来。

关键代码:在抽象类实现,其他步骤在子类实现。

应用实例: 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。 2、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。 3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。

优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。

缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。

注意事项:为防止恶意操作,一般模板方法都加上 final 关键词

模板方法(TemplateMethod)模式的更多相关文章

  1. Java 实现模板方法(TemplateMethod)模式

    类图 /** * 业务流程模板.提供基本框架 * @author stone * */ public abstract class BaseTemplate { public abstract voi ...

  2. C#中的TemplateMethod模式

    一个真实的故事 大学的时候就开过一门课程,讲设计模式,可是大学生没什么编程实践经验,在大学里面听设计模式的感觉,就像听天书.听着都有道理,可是完全领会不到其中的奥妙,大抵原因就在于没有走过弯路,没有吃 ...

  3. Template Method(模板方法)模式

    1.概述 在面向对象开发过程中,通常我们会遇到这样的一个问题:我们知道一个算法所需的关键步骤,并确定了这些步骤的执行顺序.但是某些步骤的具体实现是未知的,或者说某些步骤的实现与具体的环境相关.例子1: ...

  4. 3.设计模式----TemplateMethod模式

    模板模式,其实是一种思想,在开发中有很多地方用到模板,因为毕竟我们不可能每一个都一出一段!一个模板,填充不同,出来效果也是不一样! 准备画个时序图的,没找到工具,过几天补上! 模板模式在出现bug时候 ...

  5. 【行为型】TemplateMethod模式

    模板方法意图是为算法定义好骨架结构,并且其中的某些步骤延迟到子类实现.该模式算是较为简单的一种设计模式.在实际中,应用也较为频繁.模式的类关系图参考如下: 模式的编码结构参考如下: namespace ...

  6. java设计模式(9):模板方法模式(TemplateMethod)

    一,定义:模板方法模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 二,类图: 三,通过小例子讲解: 这个模式一般用在 ...

  7. C#设计模式系列:模板方法模式(Template Method)

    你去银行取款的时候,银行会给你一张取款单,这张取款单就是一个模板,它把公共的内容提取到模板中,只留下部分让用户来填写.在软件系统中,将多个类的共有内容提取到一个模板中的思想便是模板方法模式的思想. 模 ...

  8. C#设计模式之十四模板方法模式(Template Method)【行为型】

    一.引言 “结构型”的设计模式已经写完了,从今天我们开始讲“行为型”设计模式.现在我们开始讲[行为型]设计模式的第一个模式,该模式是[模板方法],英文名称是:Template Method Patte ...

  9. C#设计模式之十三模板方法模式(Template Method Pattern)【行为型】

    一.引言 “结构型”的设计模式已经写完了,从今天我们开始讲“行为型”设计模式.现在我们开始讲[行为型]设计模式的第一个模式,该模式是[模板方法],英文名称是:Template Method Patte ...

随机推荐

  1. 浅谈Spring解决循环依赖的三种方式

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下Spring是如果解决循环依赖的. 第一种: ...

  2. C# 中using 用来释放资源的用法

    using(...) {........} 定义了一个范围,等范围结束以后进行资源的释放. 例如: using(SqlConnection conn = new SqlConnection(" ...

  3. Node.js官方文档:到底什么是阻塞(Blocking)与非阻塞(Non-Blocking)?

    译者按: Node.js文档阅读系列之一. 原文: Overview of Blocking vs Non-Blocking 译者: Fundebug 为了保证可读性,本文采用意译而非直译. 这篇博客 ...

  4. 06. redis cluster

    目录 Redis Cluster redis cluster 特点 搭建redis cluster 访问redis cluster redis-cli 访问redis cluster 重新分片数据 新 ...

  5. VMWare虚拟机出现问题

    软件版本:VMware ESXi 5.5.0 服务器型号: Dell R720 问题经过: 某天ssh登录其中一个虚拟机,报错:kernel:BUG: soft lockup - CPU#6 stuc ...

  6. Bash Shell中的特殊位置变量及其应用

    Bash Shell中的特殊位置变量及其应用 众所周知bash shell中有许多特殊的位置变量,灵活使用它们可以更好地发挥Shell脚本的功用. 即位置变量:$1,$2,...来表示,用于让脚本在脚 ...

  7. Linux之基础系统优化

    Linux基础系统优化 >>> https://www.cnblogs.com/pyyu/p/9355477.html Linux的网络功能相当强悍,一时之间我们无法了解所有的网络命 ...

  8. 安装Visual Studio 时窗口闪过就退出

    双击安装文件,窗口闪了下就退出了. 解决方法:控制面板 ———程序——程序和功能——打开或关闭Windows功能——关闭NET相关框架. 然后再次安装即可. 参考文献:   VS安装程序一闪而过

  9. vue-loader作用

    (1)浏览器本身并不认为.vue文件,所以必须对.vue文件进行加载解析,此时需要vue-loader (2)类似的loader还有许多,如:html-loader.css-loader.style- ...

  10. 解决samba共享虚拟机第二块磁盘的难题

    linux下使用samba共享文件到window下已经实现过了   点此链接 但是共享时磁盘空间是有限的,如果空间使用完了该怎么处理呢?这是个头疼的问题,不知道samba支持不支持共享第二个文件夹,由 ...