专栏名称: ImportNew
伯乐在线旗下账号,专注Java技术分享,包括Java基础技术、进阶技能、架构设计和Java技术领域动态等。
目录
相关文章推荐
51好读  ›  专栏  ›  ImportNew

《 Head first设计模式 》学习笔记 – 模板方法模式

ImportNew  · 公众号  · Java  · 2017-02-14 20:40

正文

(点击 上方公众号 ,可快速关注)


来源:cashow,

cashow.github.io/head-first-design-patterns-notes-template-method-pattern.html

如有好文章投稿,请点击 → 这里了解详情


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


有些人没有咖啡就活不下去;有些人则离不开茶。两者共同的成分是什么?当然是咖啡因了!

但还不只这样。茶和咖啡的冲泡方式非常相似:


星巴兹咖啡冲泡法


  1. 把水煮沸

  2. 用沸水冲泡咖啡

  3. 把咖啡倒进杯子

  4. 加糖和牛奶


星巴兹茶冲泡法


  1. 把水煮沸

  2. 用沸水冲泡茶叶

  3. 把茶倒进杯子

  4. 加柠檬


下面我们用代码来创建咖啡和茶:


// 这是我们的咖啡类,用来煮咖啡

public class Coffee {

// 这是我们的咖啡冲泡法

void prepareRecipe() {

boilWater();

brewCoffeeGrinds();

pourInCup();

addSugerAndMilk();

}

// 煮沸水

private void boilWater() {

System.out.println("Boiling water");

}

// 冲泡咖啡

private void brewCoffeeGrinds() {

System.out.println("Dripping coffee through filter");

}

// 把咖啡倒进杯子

private void pourInCup() {

System.out.println("Pouring into cup");

}

// 加糖和奶

private void addSugerAndMilk() {

System.out.println("Adding Sugar and Milk");

}

}

// 这是我们的茶类,用来煮茶

public class Tea {

void prepareRecipe() {

boilWater();

steepTeaBag();

pourInCup();

addLemon();

}

// 煮沸水。这个方法和咖啡类完全一样

private void boilWater() {

System.out.println("Boiling water");

}

// 冲泡茶叶

private void steepTeaBag() {

System.out.println("Steeping the tea");

}

// 把茶倒进杯子。这个方法和咖啡类完全一样

private void pourInCup() {

System.out.println("Pouring into cup");

}

// 加柠檬

private void addLemon() {

System.out.println("Adding Lemon");

}

}


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


第一版设计


看起来这个咖啡和茶的设计相当简单,你的第一版设计,可能看起来像这样:


public abstract class CaffeineBeverage {

// prepareRecipe()方法在每个类中都不一样,所以定义成抽象方法。

abstract void prepareRecipe();

// 以下两个方法被两个子类所共享,所以被定义在这个超类中

public void boilWater() {

System.out.println("Boiling water");

}

public void pourInCup() {

System.out.println("Pouring into cup");

}

}

public class Coffee extends CaffeineBeverage {

void prepareRecipe() {

boilWater();

brewCoffeeGrinds();

pourInCup();

addSugerAndMilk();

}

private void brewCoffeeGrinds() {

System.out.println("Dripping coffee through filter");

}

private void addSugerAndMilk() {

System.out.println("Adding Sugar and Milk");

}

}

public class Tea extends CaffeineBeverage {

void prepareRecipe() {

boilWater();

steepTeaBag();

pourInCup();

addLemon();

}

private void steepTeaBag() {

System.out.println("Steeping the tea");

}

private void addLemon() {

System.out.println("Adding Lemon");

}

}


更进一步的设计


以上的设计是不是忽略了某些其他的共同点?咖啡和茶之间还有什么是相似的?


注意到两份冲泡法都采用了相同的算法:


1. 把水煮沸

2. 用热水泡咖啡或茶

3. 把饮料倒进杯子

4. 在饮料中加入适当的调料


其中,第2步和第4步并没有被抽取出来,但他们是一样的,只是应用在了不同的饮料上。我们有办法把prepareRecipe()方法也抽象化吗?让我们先从每一个子类中逐步抽象prepareRecipe()。


抽象prepareRecipe()


我们遇到的第一个问题就是,咖啡使用brewCoffeeGrinds()和addSugerAndMilk()方法,而茶使用steepTeaBag()和addLemon()方法。让我们思考这一点:浸泡(steep)和冲泡(brew)差异其实不大。所以我们给它一个新的方法名称,比方说brew(),然后不管是泡茶或者冲泡咖啡我们都用这个名称。类似地,加糖和牛奶都是在饮料中加入调料。让我们也给它一个新的方法名称:addCondiments()。这样一来,新的prepareRecipe()方法看起来像是这样:


void prepareRecipe() {

boilWater();

brew();

pourInCup();

addCondiments();

}


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


public abstract class CaffeineBeverage {

// 现在,用同一个prepareRecipe()方法来处理茶和咖啡。

// prepareRecipe()方法被声明为final,因为我们不希望子类覆盖这个方法

// 我们将第2步和第4步泛化成为brew()和addCondiments()

final void prepareRecipe() {

boilWater();

brew();

pourInCup();

addCondiments();

}

// 因为咖啡和茶处理这些方法的做法不同,所以这两个方法必须被声明为抽象,







请到「今天看啥」查看全文