策略模式
学习视频: 23种设计模式,终于有人用一个项目讲清楚了,保姆级教程通俗易懂
1. 场景:对int数组进行排序
创建一个Sorter
类对int
数组进行排序
main
方法:
public class Main {
public static void main(String[] args) {
int[] arr = {4, 2, 5, 1};
Sorter sorter = new Sorter();
sorter.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
Sorter
类:
public class Sorter {
public void sort(int[] arr) {
for (int i=0; i<arr.length; i++) {
int minPos = i;
for (int j=i+1; j<arr.length; j++) {
if (arr[j] < arr[minPos]) {
minPos = j;
}
}
swap(arr, i, minPos);
}
}
public static void swap(int[] arr, int i, int minPos) {
int temp = arr[i];
arr[i] = arr[minPos];
arr[minPos] = temp;
}
}
问题: 如果要对double类型数组进行排序怎么办?甚至如果待排序的数组是个自定义对象数组怎么办呢?总不能一直修改Sorter类,扩展sort方法吧,这不是一个好的方式。
解决办法:
- 排序核心计算是比较,比较的逻辑与Sorter类脱钩,将逻辑内嵌到待排序的对象中;
- Sorter类的比较方法的入参如何动态可变,引入接口,让比较的对象都继承此接口(comparaTo方法)。
也就是说,Sorter类和待排序的数组,通过这个接口来关联,这个接口赋予对象可比较的特性。
2. 扩展:对自定义对象数组进行排序
定义接口*(另外参考 java.lang 包中的 Comparable 接口)*
public interface Comparable<T> {
int compareTo(T o);
}
这里使用了泛型,因为 CompareTo 方法中参数类型不是固定的,使用泛型就相当于定义了一系列的接口,方便了很多
待排序的自定义对象类,继承上面接口
public class Cat implements Comparable<Cat> {
int size;
int weight;
public Cat(int size, int weight) {
this.size = size;
this.weight = weight;
}
@Override
public int compareTo(Cat c) {
return Integer.compare(this.size, c.size);
}
@Override
public String toString() {
return "Cat{" +
"size=" + size +
", weight=" + weight +
'}';
}
}
main
方法
public class Main {
public static void main(String[] args) {
Cat[] arr = {new Cat(3, 2), new Cat(4, 3), new Cat(2, 4)};
Sorter sorter = new Sorter();
sorter.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
问题:如果对象数组的比较方式不固定怎么办?比如要求 Cat
按照 weight
属性来做为比较的依据该怎么办呢? 还需要修改现有的代码,做不到对修改关闭对扩展开放。
3 再扩展: 不同的比较策略
将比较的特性和对象拆开,抽象出一个comparator
接口,Sorter
类中的排序方法分别传入对象数组和comparator
对象两个参数。
这样,对象和排序彻底解耦,对象的定义中不会涉及到比较的逻辑。
comparator
接口(java.lang包下的Comparator接口)
public interface Comparator<T> {
int compare(T a, T b);
}
接口实现
public class CatSizeComparator implements Comparator<Cat> {
@Override
public int compare(Cat a, Cat b) {
return Integer.compare(a.size, b.size);
}
}
Sorter
类
public class Sorter<T> {
public void sort(T[] arr, Comparator<T> c) {
for (int i=0; i<arr.length; i++) {
int minPos = i;
for (int j=i+1; j<arr.length; j++) {
if (c.compare(arr[j], arr[minPos]) == -1) {
minPos = j;
}
}
swap(arr, i, minPos);
}
}
public void swap(T[] arr, int i, int minPos) {
T temp = arr[i];
arr[i] = arr[minPos];
arr[minPos] = temp;
}
}
main
方法
public class Main {
public static void main(String[] args) {
Cat[] arr = {new Cat(3, 2), new Cat(4, 3), new Cat(2, 4)};
Sorter sorter = new Sorter();
sorter.sort(arr, new CatSizeComparator());
System.out.println(Arrays.toString(arr));
}
}
这样,如果要用 Cat
的 weight
属性来进行比较,就不需要修改现有的代码,只需要新加一个类继承 Comparator
接口即可。
或者更简单的直接使用匿名类,更容易扩展了,如下:
public class Main {
public static void main(String[] args) {
Cat[] arr = {new Cat(3, 2), new Cat(4, 3), new Cat(2, 4)};
Sorter<Cat> sorter = new Sorter<>();
sorter.sort(arr, (a, b) -> Integer.compare(a.weight, b.weight));
System.out.println(Arrays.toString(arr));
}
}