今天学习《Head First 设计模式》中的模板方法模式(Template Pattern),其中一节讲到数组的排序sort()使用到了模板方法模式中的技巧。由于Comparable接口与Comparator接口尚不熟悉,故趁此机会将模板方法模式在sort()方法中的体现,以及两个比较接口的使用方式与比较做一个简单总结。

书中首先给出了如下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void sort(Object[] a) {
Object aux[] = (Object[])a.clone();
mergeSort(aux, a, 0, a.length, 0);
}

private static void mergeSort(Objcet src[], Object dest[], int low, int high, int off) {
for (int i = low; i < high; i++) {
for (int j = i; j > low && ((Comparable)dest[j-1]).compareTo((Comparable)dest[j]) > 0; j--) {
swap(dest, j, j-1);
}
}
return;
}

可以看到,mergeSort()方法中基于compareTo()方法进行排序,这就要求被排序的对象给出了该方法的具体实现,也即,实现了Comparable接口中的compareTo()方法。

首先来看Comparable接口与Comparator接口

给出如下类定义:

1
2
3
4
public class Person {
String name;
int age;
}

要实现Person对象可排序,有以下两种实现方法:

Comparable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Person implements Comparable<Person> {
/**
* 属性
*/

@Override
public int compareTo(Person person) { // 实现按照Person的年龄(age)从大到小排序(也可以把逻辑反过来写实现从小到大排序)
if (this.age < person.age) {
return -1;
} else if (this.age == person.age) {
return 0;
} else {
return 1; // 则交换当前对象与person对象的位置,实现从大到小排序
}
// 也可以直接写成 return this.age.compareTo(person.age);
}
}

// 排序方式
// 事先定义了一个Person对象的数组people[]
Arrays.sort(people);

Comparator接口

1
2
3
4
5
6
7
8
// 排序方式
// 事先定义了一个Person对象的数组people[]
Arrays.sort(people, new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return p1.age - p2.age; // 若返回值为正值,则交换p1对象和p2对象的位置,实现从大到小排序
}
});

Comparable和Comparator的比较

  • 实现Comparable接口需要在类的内部实现比较方法,需要对比较类进行修改;
  • Comparable接口只能实现一种比较方式;
  • Comparator则更灵活,通过传入一个比较器对象(可以通过如上所示的匿名内部类形式),可以实现不同形式的比较方法。

为啥是模板方法模式而非策略模式

摘自《Head First 设计模式》原话:

1
在策略模式中,你所组合的类实现了整个算法。数组所实现的排序算法并不完整,它需要一个类填补compareTo()方法的实现。因此,我们认为这更像模板方法。

对于Comparable接口中的compareTo()来说,其sort()方法便是一个模板方法,其中定义了mergeSort()的实现,而需要客户提供compareTo()方法的具体实现,填补了模板方法中步骤方法的细节。