模板方法(TemplateMethod)模式
模板方法模式是准备一个抽象类,将部分逻辑以具体方法以及构造子的形式出现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑部分有不同的实现。这也是模板方法模式的用意。
模板方法模式是基于继承的代码复用的基本技术。
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)模式的更多相关文章
- Java 实现模板方法(TemplateMethod)模式
类图 /** * 业务流程模板.提供基本框架 * @author stone * */ public abstract class BaseTemplate { public abstract voi ...
- C#中的TemplateMethod模式
一个真实的故事 大学的时候就开过一门课程,讲设计模式,可是大学生没什么编程实践经验,在大学里面听设计模式的感觉,就像听天书.听着都有道理,可是完全领会不到其中的奥妙,大抵原因就在于没有走过弯路,没有吃 ...
- Template Method(模板方法)模式
1.概述 在面向对象开发过程中,通常我们会遇到这样的一个问题:我们知道一个算法所需的关键步骤,并确定了这些步骤的执行顺序.但是某些步骤的具体实现是未知的,或者说某些步骤的实现与具体的环境相关.例子1: ...
- 3.设计模式----TemplateMethod模式
模板模式,其实是一种思想,在开发中有很多地方用到模板,因为毕竟我们不可能每一个都一出一段!一个模板,填充不同,出来效果也是不一样! 准备画个时序图的,没找到工具,过几天补上! 模板模式在出现bug时候 ...
- 【行为型】TemplateMethod模式
模板方法意图是为算法定义好骨架结构,并且其中的某些步骤延迟到子类实现.该模式算是较为简单的一种设计模式.在实际中,应用也较为频繁.模式的类关系图参考如下: 模式的编码结构参考如下: namespace ...
- java设计模式(9):模板方法模式(TemplateMethod)
一,定义:模板方法模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 二,类图: 三,通过小例子讲解: 这个模式一般用在 ...
- C#设计模式系列:模板方法模式(Template Method)
你去银行取款的时候,银行会给你一张取款单,这张取款单就是一个模板,它把公共的内容提取到模板中,只留下部分让用户来填写.在软件系统中,将多个类的共有内容提取到一个模板中的思想便是模板方法模式的思想. 模 ...
- C#设计模式之十四模板方法模式(Template Method)【行为型】
一.引言 “结构型”的设计模式已经写完了,从今天我们开始讲“行为型”设计模式.现在我们开始讲[行为型]设计模式的第一个模式,该模式是[模板方法],英文名称是:Template Method Patte ...
- C#设计模式之十三模板方法模式(Template Method Pattern)【行为型】
一.引言 “结构型”的设计模式已经写完了,从今天我们开始讲“行为型”设计模式.现在我们开始讲[行为型]设计模式的第一个模式,该模式是[模板方法],英文名称是:Template Method Patte ...
随机推荐
- ES6的强大变量声明
ES6是javascript的新特性,今天来说说声明变量 过去我们声明变量,都是一个一个声明,现在有了一种新的声明方式,它可以将一个多个变量同时声明,声明后变量同时存在一个集合中,集合的数据类型是对象 ...
- uni-app 无痛刷新 token 方法
为了给用户一个流畅的体验, token 过期后需要重新请求新的 token 替换过期的 token 前端在请求接口时,和后端定义好了,如果状态码为 401 ,则表明 token 过期,需要前端请求新的 ...
- 人生物语——哲海拾贝
如今的这个社会,物欲横流.纸醉金迷.浮躁不安是这个时代的主旋律,在这样一个浮华年代的大染缸里,每个人内心都有那么一颗浮躁不安分的种子,或许它才开始发芽,或许它已经占据了你的心灵,人生当中追求 ...
- 找出所有文件最小可resize尺寸
--找出所有文件最小可resize尺寸 SELECT a.file_id, CEIL( ( NVL( hwm,1 ) * blksize ) / 1024 / 1024 ) smallest_M, C ...
- “GIS DICTIONARY A-Z” 查询页面开发(2)——INSERT INTO数据库
今日工作:数据库连接.数据写入 一.数据库连接:使用了pymysql库 from G2_dataClean import defList import pymysql db = pymysql.con ...
- centos7.7离线安装nginx
一.1.安装openssl,因为编译安装nginx需要指定openssl目录 mkdir /data/openssl -p cd /data/openssl wget https://www.open ...
- opencart按重量配送如何设置
OpenCart中基于重量的配送模块让你使用自己的价格折算表来决定配送价格.可以基于不同的重量允许设置价格范围,重量和价格的格式如下:weight(重量1):cost(价格1), weight(重量2 ...
- 【java异常】redis.clients.jedis.exceptions.JedisConnectionException: Could not get a res
产生此错误的原因通常是: 一.Redis没有启动: 我自己遇到一次这样的问题.汗! 二.由于防火墙原因无法连接到Redis; 1.服务器防火墙入站规则. 2.访问Redis的应用程序所在主机的出站规则 ...
- Pandas | 27 注意事项&窍门
警告和疑难意味着一个看不见的问题.在使用Pandas过程中,需要特别注意的地方. 与Pandas一起使用If/Truth语句 当尝试将某些东西转换成布尔值时,Pandas遵循了一个错误的惯例. 这种情 ...
- ESA2GJK1DH1K基础篇: Android实现MQTT封装源码使用说明
说明 这一节说明一下基础篇APP源码里面MyMqttCilent.java这个文件的使用 新建工程 安装MQTT的jar包 implementation 'org.eclipse.paho:org.e ...