什么是策略模式?
接下来,我们通过之前复习过的排序算法 来谈谈策略模式。
以选择排序为例,之前我们写的排序算法, 数据类型写的 Integer 类型的数组.
代码如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 交换元素
public void swap(int[] nums, int i1, int i2){
int tmp = nums[i1];
nums[i1] = nums[i2];
nums[i2] = nums[i1];
}
// 选择排序
public void selectionSort(int[] nums){
for (int i = nums.length - 1; i > 0; i--){
int maxIdx = 0;
for (int j = 1; j <= i; j++) {
if (nums[j] > nums[maxIdx]){
maxIdx = j;
}
}
swap(nums, maxIdx, i);
}
}
|
需求变化
这时,如果我们要排序 double 类型的数组呢?
这有啥难的? 直接把上述代码拷贝一份,类型换成double呗。反正Java支持函数重载,确实可以做得到。
但是有个问题,如果有大量的需要比较的类型,甚至包括自定义对象, 岂不是每一种对象都得copy一份方法, 明显不符合开闭原则 (对扩展开放,对修改闭合),因为我们没增加一个比较类,就需要对 selectionSort类修改。
改进
如何解决上边提到的问题?
我们声明一个Comparable接口,接口中有一个 compareTo(T o) 方法,为了能兼容各种类型的对象,我们使用范型.
并要求使用 selectionSort 排序的对象,必须实现 compareTo(T o) 方法.
这时,我们实现多种对象的排序上, 就会方便很多了.
代码如下 :
接口代码 :
1
2
3
4
|
// 接口
public interface Comparable<T>{
int compareTo(T o);
}
|
选择排序代码 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 交换元素
public void swap(Comparable[] nums, int i1, int i2){
Comparable tmp = nums[i1];
nums[i1] = nums[i2];
nums[i2] = tmp;
}
// 选择排序
public void selectionSort(Comparable[] nums){
for (int i = nums.length - 1; i > 0; i--){
int maxIdx = 0;
for (int j = 1; j <= i; j++) {
if (nums[j].compareTo(nums[maxIdx]) > 0){
maxIdx = j;
}
}
swap(nums,maxIdx, i);
}
}
|
需要排序的对象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public class Dog implements Comparable<Dog>{
int weight;
public Dog(int weight) {
this.weight = weight;
}
@Override
public int compareTo(Dog o) {
return this.weight - o.weight;
}
@Override
public String toString() {
return "Dog{" +
"weight=" + weight +
'}';
}
}
|
这样我们就限制了,传入的需要排序的类,必须实现 Comparable接口,并且使用 Comparable接口中的 int compareTo(T o); 方法来比较排序对象。
仍然不够灵活
但是上边这种做法,依然不够灵活. Why? 因为比如 Cat 对象中,有两个属性 height, age, 我们有时候需要用 height 排序,有时候使用 age 来排序.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class Cat implements Comparable<Cat>{
int height;
int age;
public Cat(int height, int age) {
this.height = height;
this.age = age;
}
@Override
public int compareTo(Cat o) {
return this.age - o.age;
}
@Override
public String toString() {
return "Cat{" +
"height=" + height +
", age=" + age +
'}';
}
}
|
如果采用上述 Comparable 方法, 我们没办法实现使用 height 排序。
继续改进
接下来正式引入我们的主角,策略模式,Comparator接口
策略模式符合设计模式的开闭原则 : 对修改关闭,对扩展开放
我们依然定义一个接口 Comparator接口,并且要求比较的类实现compare方法
代码如下 :
接口代码 :
1
2
3
|
public interface Comparator <T>{
int compare(T o1, T o2);
}
|
选择排序代码 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 交换元素
public void swap(T[] nums, int i1, int i2){
T tmp = nums[i1];
nums[i1] = nums[i2];
nums[i2] = tmp;
}
// 选择排序
public void selectionSort(T[] nums, Comparator<T> comparator){
for (int i = nums.length - 1; i > 0; i--){
int maxIdx = 0;
for (int j = 1; j <= i; j++) {
if (comparator.compare(nums[j], nums[maxIdx]) > 0){
maxIdx = j;
}
}
swap(nums,maxIdx, i);
}
}
|
比较代码:
1
2
3
4
5
6
7
8
9
10
|
SelectionSort sort = new SelectionSort();
Cat[] cats = {new Cat(39,1),new Cat(20,4), new Cat(10,2)};
sort.selectionSort(cats, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Cat c1 = (Cat)o1;
Cat c2 = (Cat)o2;
return c1.age - c2.age;
}
});
|
排序策略
总结 :
- 以上就是策略设计模式,在排序中的应用
- 从上到下的优化过程,可以明显的看出,从上到下依次更容易拓展
- 第一种,增加排序类型时,只能在 SelectionSort 类中增加代码. 非常不方便
- 第二种,我们在每个类内部实现 Comparable 的 compareTo方法
- 使得增加一种支持排序的类时,只需要将此类实现 Comparable 接口指定方法即可
- 而如果没有实现 compareto方法,就无法使用我们的 SelectionSort方法
- 第三种,第二种方法相比第一种来说,易扩展很多。
- 但是仍然有局限性,就是每个类只支持一种排序方法
- 当我们分别需要对 Cat 对象,用 age 排序,再用 weight 排序。
- 第二种就做不到了
- 所以第三种我们使用 比较器 Comparator, SelectionSort 排序方法中,增加一个传参数,传入一个比较器。实现对应比较方法即可
主要解决的问题