一、一个数值的排序问题引伸出JDK的Comparable接口
一个数组由整型元素组成,要求从小到达进行排序,排序类如下:
public class DataSorter {
public static void sort(int[] arr) {
for(int i = arr.length; i > 0; i--) {
for(int j = 0; j < i - 1; j++) {
if(arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
public static void print(int[] arr) {
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
客户端调用如下:
public class Client {
public static void main(String[] args) {
int[] arr = new int[]{4, 2, 1, 6, 5, 3};
DataSorter.sort(arr);
DataSorter.print(arr);
}
}
此排序类能正常的完成所需要的功能,但是只能对整型数组进行排序,如果需要对浮点型数组进行排序,就需要把sort和print方法都进行重载,如果是对自定义对象数组进行排序,也需要重载,且还需要设定排序策略,到底是依据什么来判断对象谁大谁小呢?为此,定义一个叫做Comparable的接口,如下:
public interface Comparable<T> {
public int compareTo(T t);
}
再新建一个自定义类Dog,此类要求实现Comparable接口,并定义自己的排序策略,如下:
public class Dog implements Comparable<Dog> {
private double height;
private double weight;
public Dog(double height, double weight) {
this.height = height;
this.weight = weight;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
@Override
public int compareTo(Dog dog) {
if(this == dog) {
return 0;
} else {
if(this.getHeight() > dog.getHeight()) {
return -1;
} else if(this.getHeight() < dog.getHeight()) {
return 1;
} else {
return 0;
}
}
}
@Override
public String toString() {
return "[height=" + height + ", weight=" + weight + "]";
}
}
再定义新的排序类DataSorter02,实现对任何类型的数组进行排序,只要数组元素实现了Comparable接口,如下:
public class DataSorter02 {
public static void sort(Comparable[] obj) {
for(int i = obj.length; i > 0; i--) {
for(int j = 0; j < i - 1; j++) {
if(obj[j].compareTo(obj[j + 1]) == -1) {
Comparable temp = obj[j];
obj[j] = obj[j + 1];
obj[j + 1] = temp;
}
}
}
}
public static void print(Comparable[] obj) {
for(int i = 0; i < obj.length; i++) {
System.out.print(obj[i] + " ");
}
System.out.println();
}
}
客户端对Dog类型数组进行排序,如下:
public class Client02 {
public static void main(String[] args) {
Dog[] dogs = new Dog[] {new Dog(5.0, 1.0), new Dog(4.0, 2.0), new Dog(6.0, 3.0)};
DataSorter02.sort(dogs);
DataSorter02.print(dogs);
}
}
排序后输出为:[height=4.0, weight=2.0] [height=5.0, weight=1.0] [height=6.0, weight=3.0],可见实现了排序的功能,且只要是排序的对象实现了Comparable接口并实现了compareTo方法,那么DataSorter02这个排序类就能对任意类型的数组进行排序,不用在根据要排序的对象的类型不同而重载sort和print方法,这里的Comparable接口其实就是模拟的JDK提供的Comparable接口,但是到目前为止,还不是策略模式,这里虽然对实现排序的功能统一了接口,但是排序的策略却是固定的,比如在Dog类中是按照height属性的大小进行排序的,排序的策略是写死了的,以后如果要按照其他的排序策略来进行排序又得修改程序,不具备扩展性和灵活性,所以,下面要说的就是策略模式。
二、策略模式(引伸出JDK Comparator接口)
比如要对之前的Dog类按照weight属性大小进行排序,又该怎么办呢?定义一个名叫Comparator的接口,如下:
public interface Comparator<T> {
public int compare(T t1, T t2);
}
定义一个排序的策略类,并实现Comparator接口,对Dog类按照weight属性大小进行排序,如下:
public class DogWeightComparator implements Comparator<Dog> {
@Override
public int compare(Dog d1, Dog d2) {
if(d1 == d2) {
return 0;
} else {
if(d1.getWeight() > d2.getWeight()) {
return -1;
} else if(d1.getWeight() < d2.getWeight()) {
return 1;
} else {
return 0;
}
}
}
}
然后,修改Dog类,指定一个排序的策略类,就是上面的按照weight属性进行排序的策略类,如下:
public class Dog implements Comparable<Dog> {
private double height;
private double weight;
private Comparator<Dog> dogWeightComparator;
public Dog(double height, double weight, Comparator<Dog> dogWeightComparator) {
this.height = height;
this.weight = weight;
//指定排序采用的策略
this.dogWeightComparator = dogWeightComparator;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
@Override
public int compareTo(Dog dog) {
//依据指定的排序策略进行排序
return this.dogWeightComparator.compare(this, dog);
}
@Override
public String toString() {
return "[height=" + height + ", weight=" + weight + "]";
}
}
接着排序类不变,依然使用之前的DataSorter02进行排序操作,客户端调用如下:
public class Client03 {
public static void main(String[] args) {
Comparator<Dog> comparator = new DogWeightComparator();
//传入排序的策略
Dog[] dogs = new Dog[] {new Dog(5.0, 12.0, comparator), new Dog(4.0, 2.0, comparator), new Dog(6.0, 3.0, comparator)};
DataSorter02.sort(dogs);
DataSorter02.print(dogs);
}
}
最后输出为:[height=4.0, weight=2.0] [height=6.0, weight=3.0] [height=5.0, weight=12.0],可发现实现了按照weight属性大小进行排序,以后还可以按照其它某种方式进行排序,只要排序的策略类实现了Comparator接口,将其传入即可实现排序,需要修改的仅仅只是添加新的排序策略类,实现了代码的可扩展性和灵活性。这里的Comparator接口也是模拟JDK提供的Comparator接口。
三、总结
1、策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
2、策略模式是一种定义一系列算法的方法,从概念上看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
3、策略模式的Comparator接口层次定义了一系列的可供重用的算法或行为,实现有助于析取出这些算法中的公共功能。
4、策略模式还可以简化单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。