初识设计模式 chapter 08-模板方法模式

来源:互联网 发布:淘宝转化率10%算正常吗 编辑:程序博客网 时间:2024/04/28 06:54

初识设计模式 chapter 08-模板方法模式


1 引言


直到目前,我们的议题都绕着封装转,我们已经封装了对象创建、方法调用、复杂接口、鸭子、披萨。接下来呢?我们将要深入封装算法块,好让子类可以在任何时候都可以将自己挂接进运算里。我们甚至会在本章学到一个受到好莱坞影响而启发的设计原则。

2 正文


2.1 多来点咖啡因吧


有些人没有咖啡就活不下去,有些人则离不开茶。两者的共同成分是什么,当然是咖啡因啦。
让我们扮演“代码师傅“,写一些代码来创建咖啡和茶。
下面是咖啡:

public class Coffee { /* * 这是我们的咖啡冲泡方法,直接取自训练手册。 * 每个步骤都被实现在分离的方法中。 * 这里每个方法都实现了算法中的一个步骤:煮沸水、冲泡咖啡、把咖啡倒进杯子、加糖和加奶。 */void prepareRecipe() {boilWater();brewCoffeeGrinds();pourInCup();addSugarAndMilk();} public void boilWater() {System.out.println("Boiling water");} public void brewCoffeeGrinds() {System.out.println("Dripping Coffee through filter");} public void pourInCup() {System.out.println("Pouring into cup");} public void addSugarAndMilk() {System.out.println("Adding Sugar and Milk");}}

下面是茶

public class Tea { /* * 这看起来和前一页的咖啡的实现很像,其中第2和第4个步骤不一样,但基本上是相同的冲泡法。 */void prepareRecipe() {boilWater();steepTeaBag();pourInCup();addLemon();} public void boilWater() {System.out.println("Boiling water");} public void steepTeaBag() {System.out.println("Steeping the tea");} public void addLemon() {System.out.println("Adding Lemon");} public void pourInCup() {System.out.println("Pouring into cup");}}

我们发现了重复的代码,这是好现象。这表示我们需要清理一下设计了。在这里,既然茶和咖啡是如此的相似,似乎我们应该将共同的部分抽出来,放进一个基类中。

现在我们有了新的prepareRecipe()方法,但是需要让它能够符合代码。要想这么做,我们先从CaffeineBeverage(咖啡因饮料:茶和咖啡)超类开始。

public abstract class CaffeineBeverage {/* * 现在,用同一个prepareRecipe()方法来处理茶和咖啡。 * prepareRecipe()被声明为final,因为我们不希望子类覆盖这个方法。 * 我们将步骤2和步骤4泛化为brew()和addCondiments()方法。 */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");}}

最后,我们需要处理咖啡和茶类了。这两个类现在都是依赖超类(咖啡因饮料)来处理冲泡法,所以只需要自行处理冲泡和添加调料部分。

2.2 认识模板方法


prepareRecipe()是我们的模板方法。为什么?
因为:
1、毕竟它是一个方法。
2、它用作一个算法的模板,在这个例子中,算法是用来制作咖啡因饮料的。
在这个模板中,算法内的每一个步骤都被一个方法代表了。某些方法是由这个类(超类)来处理的,某些方法则是由子类来处理。

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

2.3 对模板方法进行挂钩


钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。
钩子有好几种用途,让我们先看其中一个,稍后再看其他几个:

public abstract class CaffeineBeverageWithHook { /* * 我们加上了一个小小的条件语句,而该条件是否成立,是由一个具体方法customerWantsCondiments()决定的。 * 如果顾客“想要”调料,只有这时我们才调用addCondiments()。 */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");} /* * 我们再这里定义了一个方法,(通常)是空的缺省实现。这个方法只会返回true,不做别的事情。 * 这就是钩子,子类可以覆盖这个方法,但不见得一定要这么做。 */boolean customerWantsCondiments() {return true;}}

2.4 好莱坞原则


好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给)你。

好莱坞原则可以给我们一种防止“依赖腐败“的方法。当高层组件依赖低层组件,而低层组件又依赖高层组件,而高层组件又依赖边侧组件,依赖腐败就发生了。在这种情况下,没有人可以轻易地搞定系统是如何设计的。
在好莱坞原则之下,我们允许低层组件讲自己挂钩到系统上,但是高层组件决定什么时候和怎么样使用这些低层组件。换句话说,高层组件对待低层组件的方式是”别调用我们,我们会调用你“。

2.5 用模板方法来排序


实现compareTo()方法即可。


3 本章小结


模板方法从字面意思就很好理解,把握住新学的好莱坞原则,避免依赖腐败,这个就是本章的精髓。


1 0
原创粉丝点击