Head First 设计模式笔记 7.模板方法模式

本文介绍了模板方法模式在软件设计中的应用,通过咖啡和茶的冲泡例子展示了如何通过继承和抽象方法实现算法的模板。模板方法模式允许在超类中定义算法的基本结构,子类只需实现特定的步骤,实现了代码复用并方便算法的扩展。同时,文章提到了钩子方法,使得子类能够自定义算法的部分行为,增加了灵活性。最后,对比了模板方法模式与策略模式的区别,强调了各自在控制算法结构和实现方式上的侧重点。
摘要由CSDN通过智能技术生成

我们之前封装了对象创建,方法调用,复杂接口,鸭子,这一次,我们要封装算法块。

咖啡与茶

在设计中,我们常常会遇到一些方法通用,另外一些方法却不一样的情况。例如现在咖啡店中,茶和咖啡的冲法非常相似。我们看看。

步骤咖啡冲泡法茶冲泡法
1水煮沸 boilWater()水煮沸 boilWater()
2沸水冲泡咖啡 brewCoffeeGrinds()沸水浸泡茶叶 steepTeaBag()
3咖啡入杯 pourInCup()茶入杯 pourInCup()
4加糖和牛奶 addSugarMilk()加柠檬 addLemon()

首先,我们可以发现茶与咖啡非常相似,可以通过继承一个超类咖啡饮品CaffeinBeverage去节省重复代码。例如第一步。

其次,这些步骤很多都类似。我们可以把这些步骤封装起来,成为一个步骤prepareRecipe()。然后在超类中,实现相同的步骤,而将不同的步骤作为抽象方法,留给子类去实现。

具体来讲,我们实现了一个超类咖啡饮品CaffeinBeverage,它的制作过程如下

  1. 水煮沸
  2. 冲泡
  3. 饮料入杯
  4. 加调料

那么,让我们来实现咖啡饮品CaffeinBeverage

public abstract class CaffeinBeverage{
	// 准备饮品的步骤,煮水boilWater,冲泡brew,入杯pourIncup,加佐料addCondiments,
	// 其中煮水和入杯都一致,为具体方法。其他方法为抽象方法,交由子类实现
	// 注意这里使用了final关键字
	final void prepareRecipe(){
		boilWater();
		brew();
		pourIncup();
		addCondiments();
	}

	abstract void brew();
	
	abstract void addCondiments();
	
	void boilWater() {
		System.out.println("Boiling water");
	}
 
	void pourIncup(){
		System.out.println("Pouring into cup");
	}
	
}

有了超类,我们来实现咖啡和茶两个子类

咖啡Coffee实现

	public class Coffee extends CaffeineBeverage{
 
	public void brew() {
		System.out.println("Dripping Coffee through filter");
	}
 
	public void addCondiments() {
		System.out.println("Adding Sugar and Milk");
	}
}

茶的实现

public class Tea extends CaffeineBeverage {
	public void brew() {
		System.out.println("Steeping the tea");
	}
	public void addCondiments() {
		System.out.println("Adding Lemon");
	}
}

模板方法模式

在上述部分中,CaffeinBeverage中的prepareRecipe()实际上就是模板方法,作为一个算法的模板,某些方法由CaffeinBeverage自己实现,某些由它的子类实现。

那么这么做有什么好处呢?我们可以对比一下用不用模板方法的区别

不用模板方法用模板方法
Coffee和Tea有重复代码CaffeinBeverage可以让代码复用最大化
算法改变则Coffee和Tea都要改变算法只存在于父类,容易修改
算法的描述和实现分散在很多地方CaffeinBeverage专注算法描述,子类负责实现
新的步骤类似的饮品加入会很麻烦模板方法提供了框架,新的饮品只需要实现自己的方法就好

了解了这些,下面有请模板方法模式登场。

模板方法模式在一个方法中定义了一个算法骨架,而将一些步骤延迟到子类。模板方法可以使得子类不改变算法结构的情况下,重新定义算法中某些步骤。

以下是它的类图

图1 模板方法类图

这是模板方法类模式的具体实现框架

图2 具体实现框架

钩子

然而实际上,算法的实现可能会和模板方法不一致,有的步骤可能会被执行,有的不会。这时我们就需要用到钩子。它可以让子类有能力对算法的不同点挂钩。

例如我们可以给咖啡饮品类上个钩子,让其子类自行决定是否要加调料

public abstract class CaffeineBeverageWithHook {
 
	final void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		// 我们在这里加了一条判断函数,子类可以覆盖判断函数。从而自己决定要不要加调料
		if (customerWantsCondiments()) {
			addCondiments();
		}
	}
 
	abstract void brew();
 
	abstract void addCondiments();
 
	void boilWater() {
		System.out.println("Boiling water");
	}
 
	void pourInCup() {
		System.out.println("Pouring into cup");
	}
	
 	// 该方法就是一个钩子,子类可以覆盖它。
	boolean customerWantsCondiments() {
		return true;
	}
}

我们可以实现带钩子的咖啡,让用户自己输入yes或no,判断要不要加调料

public class CoffeeWithHook extends CaffeineBeverageWithHook {
 
	public void brew() {
		System.out.println("Dripping Coffee through filter");
	}
 
	public void addCondiments() {
		System.out.println("Adding Sugar and Milk");
	}
 
	public boolean customerWantsCondiments() {

		String answer = getUserInput();

		if (answer.toLowerCase().startsWith("y")) {
			return true;
		} else {
			return false;
		}
	}
 
	private String getUserInput() {
		String answer = null;

		System.out.print("Would you like milk and sugar with your coffee (y/n)? ");

		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		try {
			answer = in.readLine();
		} catch (IOException ioe) {
			System.err.println("IO error trying to read your answer");
		}
		if (answer == null) {
			return "no";
		}
		return answer;
	}
}

我们可以看出钩子可以让子类实现算法的可选部分

策略模式 VS 模板方法模式

策略模式和模板方法模式两者有些类似,它们都封装了算法,但是两者封装算法的目的和方法却截然不同。策略模式定义了算法家族,这些算法可以互换,用户可以使用这些不同的算法。而模板方法模式则是定义了一个算法的大纲,将某些算法由子类继承去实现。它们的不同点在于

  • 模板方法模式对算法有着更高的控制,它控制了算法的结构,而策略模式对算法没有多少控制。
  • 模板方法模式通过继承实现,而策略模式通过组合实现
  • 模板方法模式中的子类依靠超类的方法实现,而策略模式则不需要依靠任何人
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值