设计模式之模板方法模式(三)

  • 2019 年 12 月 26 日
  • 笔记

模板方法模式是一个很常见的模式,但是也需要我们拥有一双锐利的眼睛,因为模板方法有许多实现,而他们看起来并不一定和书上所讲的设计一致。

这个模式很常见是因为对创建框架来说,这个模式简直棒极了。由框架控制如何做事情,而由你(使用这个框架的人)指定框架算法中每个步骤的细节。

用模板方法排序

我们经常需要数组做什么事情?对了!排序。

Java数组类的设计者提供给我们一个方便的模板方法用来排序。让我们看看这个方法如何运行:

这里有两个方法,共同提供排序的功能,这里提供了Java的部分源码

public static void sort(Object[] a) {      Object aux[] = (Object[])a.clone();      mergeSort(aux, a, 0, a.length, 0);  }    private static void mergeSort(Object[] src, Object[] dest,int low,int high, int off) {      // 这里省略其他实现      int length = high - low;      if (length < INSERTIONSORT_THRESHOLD) {          for (int i=low; i<high; i++)              for (int j=i; j>low &&                       ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)                  swap(dest, j, j-1);          return;      }      // 这里省略其他实现  }  

排序鸭子

加入我们有一个鸭子的数组需要排序,你要怎么做?数组的排序模板方法已经提供了算法,但是你必须让这个模板方法知道如何比较鸭子。你要做的事情就是,实现一个compareTo()方法。

事情是这样的:sort()的设计者希望这个方法能使用于所有的数组,所以他们把sort变成是静态方法,这样一来,任何数组都可以使用这个方法。但是没关系,它使用起来和它被定义在超类是一样的。现在,还有一个细节要告诉你:因为sort并不是真正定义在超类中,所以sort方法需要知道你已经实现了这个compareTo方法,否则就无法进行排序。

要达到这一点,设计者利用了Comparable接口。你须实现这个接口,提供这个接口所声明的方法,也就是compareTo()。

public class Duck implements Comparable<Duck> {      String name;      int weight;        public Duck(String name, int weight) {          this.name = name;          this.weight = weight;      }        public String toString() {          return name + " weighs " + weight;      }        public int compareTo(Duck object) {            Duck otherDuck = object;            if (this.weight < otherDuck.weight) {              return -1;          } else if (this.weight == otherDuck.weight) {              return 0;          } else { // this.weight > otherDuck.weight              return 1;          }      }  }  

让我们来测试下这个程序

public class DuckSortTestDrive {        public static void main(String[] args) {          Duck[] ducks = {                          new Duck("Daffy", 8),                          new Duck("Dewey", 2),                          new Duck("Howard", 7),                          new Duck("Louie", 2),                          new Duck("Donald", 10),                          new Duck("Huey", 2)           };            System.out.println("Before sorting:");          display(ducks);            Arrays.sort(ducks);            System.out.println("nAfter sorting:");          display(ducks);      }        public static void display(Duck[] ducks) {          for (Duck d : ducks) {              System.out.println(d);          }      }  }  

观察鸭子排序的内部工作

让我们追踪Array类的sort模板方法的工作过程。我们会看到模板方法是如何控制算法的,以及在算法中的某些点上它是如何要求我们的鸭子提供某个步骤的实现的

  1. 首先我们需要鸭子数组
  2. 然后调用Array类的sort模板方法,并传入鸭子数组
  3. 想要排序一个数组,你需要一次又一次地比较两个项目,直到整个数组都排序完毕
  4. 如果鸭子的次序不对,就用Array的具体swap方法将两者对调
  5. 排序方法会持续比较并对调鸭子,直到整个数组次序是正确的

是不是很惊讶,这个也是模板方法的一种实现呢。虽然不是教科书上的模板方法,但它的实现仍然符合模板方法模式的精神。在Java的API中,还可以看到其他的。比如java.io的InputStream类有一个read()方法,是由子类实现的,而这个方法又会被read(byte b[],int off,int len)模板方法使用。

还有模板方法和策略模式都是封装组合,一个用组合,一个用继承,你搞明白了吗?如果还是太笼统的话,那么,小编请你仔细翻书《Head First设计模式》看一下这章吧,然后我们再讨论。

设计箱内的工具

总结时间到了

  • OO基础 抽象、封装、继承、多态
  • OO原则 封装变化 多用组合,少用继承 针对接口编程,不针对实现编程 为交互对象之间的松耦合设计而努力 依赖抽象,不要依赖具体类 类应该对扩展开放,对修改关闭 只和朋友交谈 别找我,我会找你(我们最新的原则提醒你,由超类主控一切,当它们需要的时候,自然会去调用子类,这就跟好莱坞一样只和朋友交谈
  • OO模式 ‘策略模式’、‘观察者模式’、‘装饰者模式’、‘抽象工厂模式’、‘工厂方法模式’、‘单例模式’、‘命令模式’、‘适配器模式’、‘外观模式’ ‘模板方法模式’在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

学习完模板方法模式,小编才知道,原来在平时频繁使用的设计模式中,他也占据着一个重要的地位,学习之前,小编是全然不知呀。这次终于get到这个模式,并且储备了知识,还需要慢慢消化噢。

下次,我们一起走进迭代器和组合模式的世界。

爱生活,爱学习,爱感悟,爱挨踢