本文示例代码材料源自Head First设计模式
以前整理自己整理的链接:
https://blog.csdn.net/u011109881/article/details/60594985

简介

模板方法模式很容易理解。思想基本如下:先在父类规定了具体的算法步骤以及算法顺序。父类可以给出部分步骤的具体实现,也可以都只给出方法框架,没有具体实现。在子类具体实现各个步骤的方法,但是各个步骤间的顺序在父类已经确定,子类无法通常不应该更改。如果规定算法顺序的方法在父类被定义成final,则子类就无法更改了。具体实现,根据实际需求确定。

  • 其目的一方面是减少代码重复,达到代码复用的目的
  • 另一方面也可以在父类控制和限制子类的动作。
  • 父类(泛化类)规定了一个算法框架,大多数时候,只要修改子类即可。
  • 将具体算法和实现相分离,各司其职(单一职责),基类负责算法设计,(某些情况也会涉及一点实际实现),子类专司具体算法实现。

模板方法模式实例1

以泡茶和泡咖啡为例。他们是一个相近的例子。
泡茶的步骤如下:

  1. 把水煮沸
  2. 用沸水泡茶叶
  3. 把茶倒进杯子
  4. 加柠檬
    而泡咖啡的步骤:
  5. 把水煮沸
  6. 用沸水泡咖啡
  7. 把咖啡倒进杯子
  8. 加糖和牛奶
    用代码很容易表示这段描述
public class Coffee {public void prepareRecipe() {boilWater();brewCoffeeGrinds();pourInCup();addSugarAndMilk();}private void addSugarAndMilk() {System.out.println("addSugarAndMilk");}private void pourInCup() {System.out.println("pourInCup");}private void brewCoffeeGrinds() {System.out.println("brewCoffeeGrinds");}private void boilWater() {System.out.println("boilWater");}}
public class Tea {public void prepareRecipe() {boilWater();steepTeaBag();pourInCup();addLemon();}private void addLemon() {System.out.println("addLemon");}private void steepTeaBag() {System.out.println("steepTeaBag");}private void pourInCup() {System.out.println("pourInCup");}private void boilWater() {System.out.println("boilWater");}}

代码很简单,但是看上去似乎存在一些冗余代码,让我们做一下结构优化。
增加一个抽象父类,然后对coffee和tea进行一些改进。

public abstract class CaffeineBeverage {public final void prepareRecipe() {//final修饰,子类无法改写boilWater();brew();pourInCup();addCondiments();}public abstract void brew() ;public abstract void addCondiments() ;private void pourInCup() {System.out.println("pourInCup");}private void boilWater() {System.out.println("boilWater");}
}
public class Coffee extends CaffeineBeverage{public void brew() {System.out.println("brewCoffeeGrinds");}public void addCondiments() {System.out.println("addSugarAndMilk ");}
}
public class Tea extends CaffeineBeverage {public void brew() {System.out.println("steepTeaBag");}public void addCondiments() {System.out.println("addLemon");}}
public class Test {public static void main(String[] args) {CaffeineBeverage tea = new Tea();tea.prepareRecipe();CaffeineBeverage coffee = new Coffee();coffee.prepareRecipe();}}

测试结果:

boilWater
steepTeaBag
pourInCup
addLemon
boilWater
brewCoffeeGrinds
pourInCup
addSugarAndMilk

可以看到,CaffeineBeverage是coffee和tea的一个泛化。

关于模板方法的hook,实例1的修改版

模板方法中基类可以存在一些称之为hook的方法,这类方法在算法步骤中属于可选部分。他们的调用与否通常由子类决定。举个例子:
对基类稍作修改

public abstract class CaffeineBeverage {public final void prepareRecipe() {//final修饰,子类无法改写boilWater();brew();pourInCup();if(customerWantsCondiments()){addCondiments();}else{System.out.println("customer don't need condiments");}}public abstract void brew() ;public abstract void addCondiments() ;private void pourInCup() {System.out.println("pourInCup");}private void boilWater() {System.out.println("boilWater");}boolean customerWantsCondiments(){//该方法为hook方法,子类可以改写它,也可以不改。return true;}
}

对于实现类,可根据需要决定是否覆盖hook接口
比如Coffee类就修改了

public class Coffee extends CaffeineBeverage{public void brew() {System.out.println("brewCoffeeGrinds");}public void addCondiments() {System.out.println("addSugarAndMilk ");}boolean customerWantsCondiments() {String answer = getUserInput();if(answer.startsWith("y")||answer.startsWith("Y")){return true;}else{return false;}}String getUserInput(){System.out.println("do you want condiments?(y/n)");BufferedReader in = new BufferedReader(new InputStreamReader(System.in));String answer = null;try {answer = in.readLine();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return answer;}
}

而Tea类则没有

public class Tea extends CaffeineBeverage {public void brew() {System.out.println("steepTeaBag");}public void addCondiments() {System.out.println("add condiment directly");System.out.println("addLemon");}}

测试类则无需变动

public class Test {public static void main(String[] args) {CaffeineBeverage tea = new Tea();tea.prepareRecipe();System.out.println();CaffeineBeverage coffee = new Coffee();coffee.prepareRecipe();}}

从测试结果可以看到,如果是tea则无需问是否添加调料,而在coffee时,却需要用户确认。

实际使用的模板方法很可能并不像上面的例子这样容易辨识。比如,如果我们有一个User的ArrayList,需要对User的age进行排序,那么可以用如下方式实现
大概框架:

     ArrayList<User> list = new ArrayList();list.sort(new Comparator<User>() {public int compare(User o1, User o2) {return 0;}});

这里其实用到的就是模板方法,很费解吧。
以下纯属个人理解,如有错误请指出:
ArrayList的排序是用Arrays的静态sort方法来实现的
这里的写法其实是实现了在Comparator中的hook接口:compare方法
sort中会使用两个comparable的对象进行compare,而实际compare时则调用的是我们上面实现的compare方法。
模板方法用的高深起来,也很难懂呢,所以其他有很多我们没有注意到的地方,都隐藏着设计模式的影子,等着我们发现呀。


Android种代码跟踪view的draw方法其实也是用的模板设计模式

Head First设计模式读书笔记七 第八章 模板方法模式相关推荐

  1. 大话设计模式读书笔记(十三) 状态模式

    状态模式: 状态模式定义: 状态模式(State):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像改变了其子类. 状态模式UMl类图: 状态模式Java代码实现 public class ...

  2. Head First设计模式读书笔记九 第十章 状态模式

    过去的笔记链接 https://blog.csdn.net/u011109881/article/details/60158137 状态模式实例 用Java设计糖果机吧 大致流程: 上图中,有四种状态 ...

  3. Head First设计模式读书笔记四 简单工厂 工厂模式 抽象工厂模式

    本文示例代码材料源自Head First设计模式 以前整理自己整理的链接: 工厂模式 https://blog.csdn.net/u011109881/article/details/56541580 ...

  4. android工厂模式和策略模式,android 源码设计模式读书笔记(四)工厂模式和策略模式...

    把这两个一起写 因为他们两个UML的代码接口非常的相似 工厂模式代码结构图 image.png 策略模式UML image.png 在我们看完结构图后 感觉位移不同的就是Factory个Context ...

  5. 步步为营 .NET 设计模式学习笔记 七、Proxy(代理模式)

    概述 在软件系统中,有些对象有时候由于跨越网络或者其他的障碍,而不能够或者不想直接访问另一个对象,如果直接访问会给系统带来不必要的复杂性,这时候可以在客户程序和目标对象之间增加一层中间层,让代理对象来 ...

  6. 设计模式读书笔记-----工厂方法模式

    一.问题 在前一章<设计模式读书笔记-----简单工厂模式>中通过披萨的实例介绍了简单工厂模式.在披萨实例中,如果我想根据地域的不同生产出不同口味的披萨,如纽约口味披萨,芝加哥口味披萨.如 ...

  7. 大话设计模式读书笔记

    主题 概要 设计模式 大话设计模式读书笔记 编辑 时间 新建 20170423 序号 参考资料 1 大话设计模式 重新看了一遍设计模式,除了一些已经特别熟悉的模式,都自己敲了一遍代码,有些豁然开朗的感 ...

  8. JavaScript设计模式读书笔记(一)= 创建型设计模式

    全系列目录 JavaScript设计模式读书笔记(一)=> 创建型设计模式 JavaScript设计模式读书笔记(二)=> 结构型设计模式 JavaScript设计模式读书笔记(三)=&g ...

  9. JavaScript设计模式读书笔记(四)= 技巧型设计模式

    全系列目录 JavaScript设计模式读书笔记(一)=> 创建型设计模式 JavaScript设计模式读书笔记(二)=> 结构型设计模式 JavaScript设计模式读书笔记(三)=&g ...

最新文章

  1. 惠斯通电桥信号调理芯片_用惠斯通电桥测电阻
  2. SpringBoot应用部署于外置Tomcat容器
  3. python自学什么书比较好-如何自学Python ?自学看什么书比较好?
  4. Gartner表示:2017年全球IT支出上升2.7% 中国IT支出达到2.34万亿
  5. jmeter时间函数格林尼治时间_Jmeter时间函数工具(参考)
  6. 避免活跃性危险(第十章)
  7. Modularity(模块化-AMD规范)
  8. 小米POCO X3今日亮相:首发骁龙732G后置6400万四摄
  9. python爬小说目录_【python入门爬虫】爬取笔趣阁小说
  10. 内存卡无法格式化 修复
  11. Effective Kotlin 中文版
  12. 写着代码的插画师—王凌 |WLOP|
  13. 阿里巴巴短信验证码使用
  14. 写尽自己一个人的孤独却写不出心里的寂寞
  15. python 视频加字幕_Python追风者之视频编辑moviepy
  16. dellR730装机教程
  17. 管理系统开发的常见软件
  18. java 三点定位_GPS定位系统(三)——Java后端
  19. 快收藏!最适合计算机大学生的Java毕业设计项目--音乐视频网站系统!
  20. ov2604寄存器配置

热门文章

  1. string常用函数用法集合
  2. 使用jsp实现文件上传的功能
  3. 【JZOJ4920】【NOIP2017提高组模拟12.10】降雷皇
  4. LeetCode Min Stack 最小值栈
  5. mysql安全性特点_MySQL数据库有哪些特点?为何能得到了广泛应用?
  6. 动态规划——环形子数组的最大和(Leetcode 918)
  7. python中用于绘制各种图形的区域称作_Python--matplotlib绘图可视化知识点整理(示例代码)...
  8. 国产数据库发展十策(三):是走MySQL路线还是PostgreSQL路线?
  9. 数据3分钟丨Databricks与Snowflake开撕;阿里云多款自研数据库支撑首个“100%云上双11”...
  10. 阿里云数据库产品专家胡航丽:数据库自动驾驶平台DAS重磅助力数据库领域智能未来...