100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 《Head First 设计模式》之模板方法模式——冲泡咖啡和茶

《Head First 设计模式》之模板方法模式——冲泡咖啡和茶

时间:2023-05-11 16:02:37

相关推荐

《Head First 设计模式》之模板方法模式——冲泡咖啡和茶

模板方法模式(Template)

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

好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给)你。要点:模板方法的抽象类可以定义具体方法、抽象方法和钩子。抽象方法由子类实现。钩子是一种方法,在抽象类中不做事,或只做默认的事,子类可以选择要不要覆盖它。为了防止子类改变模板方法中的算法,可以将模板方法声明为final。好莱坞原则告诉我们,将决策权放在高层模块中,以便决定如何以及何时调用低层模块。策略模式和模板方法模式都封装算法,一个用组合,一个用继承。工厂方法(由子类决定实例化哪个具体类)是模板方法(子类决定如何实现算法中的步骤)的一种特殊版本。

示例:

咖啡冲泡法

把水煮沸用沸水冲泡咖啡把咖啡倒进杯子加糖和牛奶

茶冲泡法

把水煮沸用沸水冲泡茶叶把茶倒进杯子加柠檬

茶和咖啡是如此得相似,似乎我们应该将共同的部分抽取出来,放进一个基类中。

1 public abstract class CaffeineBeverage { 2// 现在,用同一个prepareRecipe()方法来处理茶和咖啡。 3// prepareRecipe()方法被声明为final,因为我们不希望子类覆盖这个方法 4// 我们将第2步和第4步泛化成为brew()和addCondiments() 5final void prepareRecipe() { 6 boilWater(); 7 brew(); 8 pourInCup(); 9 addCondiments();10}11 12// 因为咖啡和茶处理这些方法的做法不同,所以这两个方法必须被声明为抽象,13// 剩余的东西留给子类去操心14abstract void addCondiments();15abstract void brew();16 17public void boilWater() {18 System.out.println("Boiling water");19}20 21public void pourInCup() {22 System.out.println("Pouring into cup");23}24 }

让我们细看抽象类是如何被定义的,包括了它内含的模板方法和原语操作。

1 // 这就是我们的抽象类。它被声明为抽象,用来作为基类,其子类必须实现其操作 2 public abstract class AbstractClass { 3// 这就是模板方法。它被声明为final,以免子类改变这个算法的顺序。 4final void templateMethod() { 5 // 模板方法定义了一连串的步骤,每个步骤由一个方法代表 6 primitiveOperation1(); 7 primitiveOperation2(); 8 concreteOperation(); 9}10 11// 在这个范例中有两个原语操作,具体子类必须实现它们12abstract void primitiveOperation1();13abstract void primitiveOperation2();1415// 这个抽象类有一个具体的操作。16void concreteOperation() {17 // ...18}19 }

接着需要处理咖啡和茶类,这两个类现在都是依赖超类来处理冲泡法,所以只需要自行处理冲泡和添加调料部分:

1 public class Coffee extends CaffeineBeverage { 2@Override 3void brew() { 4 System.out.println("Dripping coffee through filter"); 5} 6 7@Override 8void addCondiments() { 9 System.out.println("Adding Sugar and Milk");10}11 }12 13 public class Tea extends CaffeineBeverage {14@Override15void brew() {16 System.out.println("Steeping the tea");17}18 19@Override20void addCondiments() {21 System.out.println("Adding Lemon");22}23 }

钩子的使用

1 public abstract class CaffeineBeverageWithHook { 2final void prepareRecipe() { 3 boilWater(); 4 brew(); 5 pourInCup(); 6 // 我们加上了一个小小的条件语句,而该条件是否成立, 7 // 是由一个具体方法customerWantsCondiments()决定的。 8 // 如果顾客“想要”调料,只有这时我们才调用addCondiments()。 9 if (customerWantsCondiments()) {10 addCondiments();11 }12}13 14abstract void addCondiments();15abstract void brew();16 17public void boilWater() {18 System.out.println("Boiling water");19}20 21public void pourInCup() {22 System.out.println("Pouring into cup");23}24 25// 我们在这里定义了一个方法,(通常)是空的缺省实现。这个方法只会返回true,不做别的事。26// 这就是一个钩子,子类可以覆盖这个方法,但不见得一定要这么做。27boolean customerWantsCondiments() {28 return true;29}30 }

用模板方法排序

Java数组类的设计者提供给我们一个方便的模板方法用来排序。必须实现Comparable接口,提供这个接口所声明的compareTo()方法。

其他模板方法实例

java.io的InputStream类有一个read()方法,是由子类实现的,而这个方法又会被read(byte b[], int off, int len)模板方法使用。Swing的JFrame继承了一个paint()方法。在默认状态下,paint()是不做事情的,因为它是一个“钩子”。通过覆盖paint(),可以将自己的代码插入JFrame的算法中,显示出想要的画面。applet是一个能在网页上面执行的小程序。任何applet必须继承自Applet类,而Applet类中提供了好些钩子。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。