文档章节

HeadFirst设计模式(九) - 模板方法模式

XuePeng77
 XuePeng77
发布于 2016/08/04 22:56
字数 1385
阅读 23
收藏 1

直接举个例子

    来看看某咖啡厅里,茶和咖啡的冲泡方式。具体如下:

    咖啡的冲泡法:

  1. 把水煮沸;
  2. 用沸水冲泡咖啡;
  3. 把咖啡倒进杯子;
  4. 加糖和牛奶;

    茶的冲泡法:

  1. 把水煮沸;
  2. 用沸水侵泡茶叶;
  3. 把茶倒进杯子;
  4. 加柠檬;

用代码实现上面的例子

public class Coffee {

	public Coffee() {
		boilWater();
		brewCoffeeGrinds();
		pourInCup();
		addSugarAndMilk();
	}
	
	public void boilWater() {
		System.out.println("把水煮沸...");
	}

	public void brewCoffeeGrinds() {
		System.out.println("用沸水冲泡咖啡...");
	}

	public void pourInCup() {
		System.out.println("把咖啡倒进杯子...");
	}

	public void addSugarAndMilk() {
		System.out.println("加糖和牛奶...");
	}
}
public class Tea {

	public Tea() {
		boilWater();
		steepTeaBag();
		pourInCup();
		addLemon();
	}
	
	public void boilWater() {
		System.out.println("把水煮沸...");
	}

	public void steepTeaBag() {
		System.out.println("用沸水侵泡茶叶...");
	}

	public void pourInCup() {
		System.out.println("把茶倒进杯子...");
	}

	public void addLemon() {
		System.out.println("加柠檬...");
	}
}

    上面两个类分别是咖啡类与茶类,我们可以看到,两个类中有重复的代码,这表示我们需要清理一下设计了。在这里,既然茶和咖啡师如此地相似,似乎我们应该将共同的部分抽取出来,放进一个基类中。

抽取相同的部分

    由于咖啡与茶都含有咖啡因,所以我们抽象出一个咖啡因对象作为抽象基类,在制作咖啡与茶的过程中,boilWater()方法和pourlnCup()方法是相同的,所以在基类中实现,其余的不同函数则在子类中实现。

    在基类中,定义了一个抽象方法prepareRecipe(),它负责调用具体的制作步骤,由子类自己实现。

    在上面这个设计中,还可以更进一步,在观察一下,咖啡与茶还有什么共同点呢?注意,两种饮品都采用了相同的冲泡方法:

  1. 把水煮沸;
  2. 用热水冲泡咖啡或者茶;
  3. 把饮品倒入杯子;
  4. 添加适当的调料;

    那么,我们也将prepareRecipe()方法进行抽象,现在就来看看怎么做……

    将冲泡定义一个抽象方法brew(),在将添加调料定义一个抽象方法addCondiments(),最后将抽象方法prepareRecipe()进行修改,让他负责冲泡步骤,新的CaffeineBeverage类看起来就像这样:

public abstract class CaffeineBeverage {
	
	// 相同的步骤在基类中做  
	public void boilWater() {
		System.out.println("把水煮沸...");
	}
	
	public void pourInCup() {
		System.out.println("把饮品倒进杯子...");
	}
	
	// 不同的步骤由子类自己实现
	public abstract void brew();
	public abstract void addCondiments();
	
	// 基类负责对每一步进行调用
	final void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		addCondiments();
	}
}
package cn.net.bysoft.template;

public class Coffee extends CaffeineBeverage {
	@Override
	public void brew() {
		System.out.println("用沸水冲泡咖啡...");
	}
	@Override
	public void addCondiments() {
		System.out.println("加糖和牛奶...");
	}
}

package cn.net.bysoft.template;

public class Tea extends CaffeineBeverage {
	@Override
	public void brew() {
		System.out.println("用沸水侵泡茶叶...");
	}

	@Override
	public void addCondiments() {
		System.out.println("加柠檬...");
	}
}

    在上面的代码中,我们所做的第一件事就是把原来brewCoffeeGrinds()方法和steepTeaBag()方法进行了抽象,接着把addSugarAndMilk()方法和addLemon()方法也进行了抽象。接下来将冲泡的步骤调用封装到了基类中的prepareRecipe()方法里,最后重写了Coffee对象和Tea对象。下面进行测试:

public class Client {
	public static void main(String[] args) {
		// 泡一杯茶
		Tea tea = new Tea();
		tea.prepareRecipe();
		
		System.out.println("\n");
		
		// 冲一杯咖啡
		Coffee coffee = new Coffee();
		coffee.prepareRecipe();
	}
}

    测试成功,现在冲泡的步骤调用由CaffeineBeverage类主导,它拥有算法,而且保护这个算法。对子类来说,CaffeineBeverage类的存在可以将代码的复用最大化。算法只存在于一个地方,所以容易修改。相同的算法有基类实现,不同的算法由子类提供完整的实现。

定义模板方法模式

    这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这可以确保算法的结构保持不变,同时由子类提供部分实现。

使用钩子

    钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩由子类决定。

    根据上面的业务来举例,在是否添加调料时对客户进行询问,如果客户需要在添加调料。

    在上图中,基类加入了是否添加调料的钩子方法,在添加调料之间调用,算法还是由prepareRecipe()方法管理,Coffee类重写了钩子函数,通过命令行向用户确认是否要添加调料,回复y/n。而茶类没有重写钩子函数,则调用缺省函数,调用ture,每次都添加调料。下面进行测试:

public class Client {
	public static void main(String[] args) {
		// 泡一杯茶
		Tea tea = new Tea();
		tea.prepareRecipe();
		
		System.out.println("\n");
		
		// 冲一杯咖啡
		Coffee coffee = new Coffee();
		coffee.prepareRecipe();
		
		System.out.println("\n");
		
		// 再冲一杯咖啡
		Coffee coffee2 = new Coffee();
		coffee2.prepareRecipe();
	}
}

好莱坞原则

别调用我们,我们会调用你。

    好莱坞原则可以给我们一种防止“依赖腐败” 的方法。在好莱坞原则之下,我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎么使用这些低层组件。换句话说,高层组件对待低层组件的方式是“别调用我们,我们会调用你”。

© 著作权归作者所有

共有 人打赏支持
XuePeng77
粉丝 42
博文 135
码字总数 178151
作品 0
丰台
私信 提问
【设计模式笔记】(十六)- 代理模式

一、简述 代理模式(Proxy Pattern),为其他对象提供一个代理,并由代理对象控制原有对象的引用;也称为委托模式。 其实代理模式无论是在日常开发还是设计模式中,基本随处可见,中介者模式中...

MrTrying
2018/06/24
0
0
(目录)设计模式(可复用面向对象软件的基础)

本系列“设计模式”博客使用Golang语言实现算法。所谓算法是指解决一个问题的步骤,个人觉得不在于语言。小弟只是最近学习Golang,所以顺带熟练一下语法知识,别无它意。 本系列博客主要介绍...

chapin
2015/01/13
0
0
设计模式15——Template Method设计模式

Template Method模板方法设计模式定义一个操作中算法的骨架,将具体步骤的执行延迟到子类中实现。Java中的抽象类就是使用了模板方法设计模式。模板方法设计模式结构如下: 以文档处理为例,T...

小米米儿小
2014/01/24
0
0
设计模式Java Design Pattern-工厂方法模式FactoryMethod

我的博客 一、 设计模式的分类 大体可以分为三类: 创建型模式(5个) 单例模式、原型模式、工厂方法模式、抽象工厂模式、建造者模式 结构性模式(7个) 适配器模式、装饰器模式、代理模式、...

勇敢写信
2018/03/22
0
0
[设计模式]简单工厂模式

简介 简单工厂模式 (Simple Factory) 又叫静态工厂方法(Static Factory Method)模式。 简单工厂模式通常是定义一个工厂类,这个类可以根据不同变量返回不同类的产品实例。 简单工厂模式是一...

静默虚空
2015/06/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Navicat怎样导入Excel表格和txt文本的数据

Navicat怎样导入Excel表格和txt文本的数据 2018年07月02日 11:29:11 零碎de記憶 阅读数:2433 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39135287/ar...

linjin200
6分钟前
0
0
使用MaxCompute Java SDK运行安全相关命令

使用MaxCompute Console的同学,可能都使用过MaxCompute安全相关的命令。官方文档上有详细的MaxCompute 安全指南 ,并给出了安全相关语句汇总 。 简而言之, 权限管理 、 列级别访问控制 、 ...

阿里云云栖社区
11分钟前
0
0
中小公司的Java工程师应该如何逆袭冲进BAT?

(1)80% Java工程师都有的迷茫 这篇文章,跟大家聊一聊很多很多很多人问我的一个问题:中小公司的Java工程师应该如何规划准备,才能跳槽进入BAT这类一线互联网公司? 之所以我用了三个 “很...

Java填坑路
12分钟前
1
0
你的应用够安全吗?绿标2.0隐私权限详解

近日,最新一期的《绿色应用达标率调查报告》结果显示,应用在安全方面的通过率仅为57%,相较于其他四项标准通过率最低。其中隐私权限的过度获取是主要原因之一,需要开发者尽快完成整改。 ...

安卓绿色联盟
22分钟前
0
0
使用MaxCompute Java SDK运行安全相关命令

使用MaxCompute Console的同学,可能都使用过MaxCompute安全相关的命令。官方文档上有详细的MaxCompute安全指南,并给出了安全相关语句汇总。 简而言之,权限管理、列级别访问控制、项目空间...

阿里云官方博客
26分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部