JAVA 设计模式 模板方法模式

定义

模板方法模式 (Template Method)

定义了一个操作中的算法的骨架,而将部分步骤的实现在子类中完成。 模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模板方法模式是所有模式中最为常见的几个模式之一,是基于继承代码复用的基本技术。,没有关联关系。

因此,在模板方法模式的类结构图中,只有继承关系

模板方法模式需要开发抽象类和具体子类的设计师之间的协作。一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤。

代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是从此而来。

结构

图-模板方法模式结构图

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

abstract class AbstractClass {
 public abstract void PrimitiveOperation1();
 public abstract void PrimitiveOperation2();
 
 public void TemplateMethod() {
         PrimitiveOperation1();
         PrimitiveOperation2();
     }
 }

ConcreteClass : 实现实现父类所定义的一个或多个抽象方法。

class ConcreteClassA extends AbstractClass {
     @Override
 public void PrimitiveOperation1() {
         System.out.println("具体A类方法1");
     }
 
     @Override
 public void PrimitiveOperation2() {
         System.out.println("具体A类方法2");
     }
 }
 
 class ConcreteClassB extends AbstractClass {
     @Override
 public void PrimitiveOperation1() {
         System.out.println("具体B类方法1");
     }
 
     @Override
 public void PrimitiveOperation2() {
         System.out.println("具体B类方法2");
     }    
 }

测试代码

public class TemplateMethodPattern {
 public static void main(String[] args) {
         AbstractClass objA = new ConcreteClassA();
         AbstractClass objB = new ConcreteClassB();    
         objA.TemplateMethod();
         objB.TemplateMethod();
     }
 }

要点

模板方法模式中的三类角色 1、具体方法(Concrete Method)

2、抽象方法(Abstract Method)

3、钩子方法(Hook Method)

三类角色的关联

在模板方法模式中,首先父类会定义一个算法的框架,即实现算法所必须的所有方法。

其中,具有共性的代码放在父类的具体方法中。

各个子类特殊性的代码放在子类的具体方法中。但是父类中需要有对应抽象方法声明。

钩子方法可以让子类决定是否对算法的不同点进行挂钩。

总结

使用模板方法模式可以将代码的公共行为提取,以达到复用的目的。

而对于特殊化的行为在子类中实现。父类的模板方法可以控制子类中的具体实现。

子类无需了解整体算法框架,只需实现自己的业务逻辑即可。

实例

模板方法模式应用场景十分广泛。

在《Head First》的模板方法模式章节里列举了一个十分具有代表性的例子。 

现实生活中,茶和咖啡是随处可见的饮料。冲泡一杯茶或冲泡一杯咖啡的过程是怎样的?

我们来整理一下流程。

泡茶: 烧开水 ==> 冲泡茶叶 ==> 倒入杯中 ==> 添加柠檬 泡咖啡: 烧开水 ==> 冲泡咖啡 ==> 倒入杯中 ==> 添加糖和牛奶

由以上处理步骤不难发现,准备这两种饮料的处理过程非常相似。我们可以使用模板类方法去限定制作饮料的算法框架。

其中相同的具有共性的步骤(如烧开水、倒入杯中),直接在抽象类中给出具体实现。

而对于有差异性的步骤,则在各自的具体类中给出实现。

抽象类

abstract class Beverage {
 
 // 模板方法,决定了算法骨架。相当于TemplateMethod()方法
  public void prepareBeverage() {
         boilWater();
         brew();
         pourInCup();
 if (customWantsCondiments())
         {
             addCondiments();
         }
     }
 
 // 共性操作,直接在抽象类中定义
  public void boilWater() {
         System.out.println("烧开水");
     }
 
 // 共性操作,直接在抽象类中定义
  public void pourInCup() {
         System.out.println("倒入杯中");
     }
 
 // 钩子方法,决定某些算法步骤是否挂钩在算法中
  public boolean customWantsCondiments() {
 return true;
     }
 
 // 特殊操作,在子类中具体实现
  public abstract void brew();
 
 // 特殊操作,在子类中具体实现
  public abstract void addCondiments();
 
 }

具体类

class Tea extends Beverage {
 
     @Override
 public void brew() {
         System.out.println("冲泡茶叶");
     }
 
     @Override
 public void addCondiments() {
         System.out.println("添加柠檬");
     }
 
 }
 
 class Coffee extends Beverage {
 
     @Override
 public void brew() {
         System.out.println("冲泡咖啡豆");
     }
 
     @Override
 public void addCondiments() {
         System.out.println("添加糖和牛奶");
     }
 
 }

测试代码

public static void main(String[] args) {
 
     System.out.println("============= 准备茶 =============");
     Beverage tea = new Tea();
     tea.prepareBeverage();
 
     System.out.println("============= 准备咖啡 =============");
     Beverage coffee = new Coffee();
     coffee.prepareBeverage();
 
 }

运行结果

============= 准备茶 =============
 烧开水
 冲泡茶叶
 倒入杯中
 添加柠檬
 ============= 准备咖啡 =============
 烧开水
 冲泡咖啡豆
 倒入杯中
 添加糖和牛奶

推荐

本文属于 JAVA设计模式系列

参考资料

《大话设计模式》 《HeadFirst设计模式》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python数据科学

使用Pandas&NumPy进行数据清洗的6大常用方法

数据科学家花了大量的时间清洗数据集,并将这些数据转换为他们可以处理的格式。事实上,很多数据科学家声称开始获取和清洗数据的工作量要占整个工作的80%。

27920
来自专栏阿凯的Excel

筛选功能(Pandas读书笔记9)

今天和大家分享如果使用Pandas实现单、多条件筛选、模糊筛选。 还是老套路,我们需要先读取一组数据作为测试文件。 测试文件使用读书笔记7的材料,传送门如下: ...

1.4K60
来自专栏青玉伏案

代码重构(二):类重构规则

在上篇博客《代码重构(一):函数重构规则(Swift版)》中,详细的介绍了函数的重构规则,其中主要包括:Extract Method, Inline Metho...

237100
来自专栏数说工作室

class 类—老司机的必修课 | 统计师的Python日记 第11课

本文是【统计师的Python日记】第11天的日记 回顾一下: 第1天学习了Python的基本页面、操作,以及几种主要的容器类型。 第2天学习了python的函...

378100
来自专栏计算机视觉与深度学习基础

HDU2066

神坑的题目 思路就是枚举起点,迪杰斯特拉求最短路径,再枚举终点(如果起点终点一起枚举可能会超时,也能勉强扯上动态规划的思想吧),求最短路径。 如果剪枝可以加一个...

24370
来自专栏小樱的经验随笔

基数排序与桶排序,计数排序【详解】

桶排序简单入门篇^-^ 在我们生活的这个世界中到处都是被排序过的东东。站队的时候会按照身高排序,考试的名次需要按照分数排序,网上购物的时候会按照价格排序,电子邮...

38370
来自专栏Java爬坑系列

【JAVA零基础入门系列】Day3 Java基本数据类型

  前两篇已经将开发环境搭建完成,如果你已经按之前的教程按部就班的完成了部署,那么世界上最优秀的编程语言之一和世界上最优秀的IDE之一已经出现在你的电脑上(此处...

23580
来自专栏工科狗和生物喵

【计算机本科补全计划】C++牛客网试题习题解析

正文之前 一大早醒来,外面淅淅沥沥的雨绵绵的下着,床铺真的舒服,但是我也不能就在床上刷微博看小说吧,所以想起了昨晚下载的牛客网的APP,赶紧掏出我的大宝贝---...

40070
来自专栏java一日一条

使用Java 8函数式编程生成字母序列

在 Java 8 中使用函数式编程生成字母序列是一个很大的挑战。Lukas Eder 愉快地接受了这个挑战,他将告诉我们如何使用 Java 8 来生成ABC的序...

9120
来自专栏coding for love

JS进阶系列03-JS面向对象的三大特征之多态

多态是同一个行为具有多个不同表现形式或形态的能力。在JAVA中,多态通过在子类中重写父类方法去实现。但是在JS中,由于JS本身是动态的,天生就支持多态。大家可以...

11020

扫码关注云+社区

领取腾讯云代金券