zoukankan      html  css  js  c++  java
  • 《Head First 设计模式》之模板方法模式——冲泡咖啡和茶

    模板方法模式(Template)

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

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

    示例:

    咖啡冲泡法

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

    茶冲泡法

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

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

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

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

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

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

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

     

    钩子的使用

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

     


     

    用模板方法排序

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

    其他模板方法实例

    1. java.io的InputStream类有一个read()方法,是由子类实现的,而这个方法又会被read(byte b[], int off, int len)模板方法使用。
    2. Swing的JFrame继承了一个paint()方法。在默认状态下,paint()是不做事情的,因为它是一个“钩子”。通过覆盖paint(),可以将自己的代码插入JFrame的算法中,显示出想要的画面。
    3. applet是一个能在网页上面执行的小程序。任何applet必须继承自Applet类,而Applet类中提供了好些钩子。
  • 相关阅读:
    浅析锂电池保护板(BMS)系统设计思路(一)
    手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务(三)
    手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务(二)
    手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务(一)
    手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务调度策略(五)
    手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务调度策略(四)
    手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务调度策略(三)
    精度要求较高的场景对float和double浮点数据的处理
    小数形式的十进制数字转换为二进制(附过程)
    mysql 隐式转换问题(案例二)
  • 原文地址:https://www.cnblogs.com/-1307/p/6441182.html
Copyright © 2011-2022 走看看