对整型数组排序
假设现在我们要写一个类,其 sort()
方法可以接收一个整型数组,然后对该整型数组进行排序。那么排序部分的代码就差不多应该这样写:
public class Sort {
public static void sort(int[] arr) {
int i, j = 0;
int temp;
for (i = 0; i < arr.length; i++) {
for (j = 1; j < arr.length - i; j++) {
//如果前面的元素大于后面的,则交换位置
if (arr[j - 1] > arr[j]) {
temp = arr[j];
arr[j] = arr[j - 1];
arr[j-1] = temp;
}
}
}
}
}

从截图可以看到现在完全满足了整型排序的需求。
对自定义类数组排序
假设现在我们需要变更一下需求,要实现对 Person 类数组按身高大小进行排序,那么现在的排序方法显然不能满足。
public class Person {
//身高
private int height;
//体重
private int weight;
//其他方法省略
}
对于这个需求,我们可能会想到让每个类都继承Comparable 接口并实现其 compareTo() 方法,定义其比较规则。那么代码就要更改为如下所示:
public class Person implements Comparable<Person>{
//身高
private int height;
//体重
private int weight;
//其他方法省略
@Override
public int compareTo(Person o) {
if (o.height > height) {
return -1;
} else if (o.height < height) {
return 1;
} else {
return 0;
}
}
}
public class Sort{
public void sort(Comparable[] arr) {
int i, j = 0;
Comparable temp;
for (i = 0; i < arr.length; i++) {
for (j = 1; j < arr.length - i; j++) {
//如果前面的元素大于后面的,则交换位置
if (arr[j - 1].compareTo(arr[j]) > 0) {
temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
}
}

到这里,我们只要为我们想要排序的类添加比较规则,那么就都能实现排序。例如现在对猫排序:
public class Cat implements Comparable<Cat> {
//体重
private int weight;
//身体长度
private int length;
//其他代码省略
@Override
public int compareTo(Cat o) {
if (o.length < length) {
return 1;
} else if (o.length > length) {
return -1;
} else {
return 0;
}
}
}

现在这中方法看起来已经可以实现对不同类型对象进行排序了,但灵活性还是不够高,还是需要进行优化。
假设现在我们对 Person 类排序时重新定义规则,按照其 weight 属性的大小进行排序,那么现有的这种继承 Comparable 接口的方式就不能很好满足需求,不得不对 Person 类的 compareTo() 进行修改。
在设计模式中,一个非常重要的原则是 开闭原则,即对扩展开放,对修改关闭。很显然上述通过修改类中方法就不满足开闭原则。所以有没有更好的方式做到对 Person 类进行排序呢?
答案是肯定的,那就是把每个类的比较规则从类中分离出来成为一个单独的类,像下面这样(以Person类为例):
//Person类的比较器
public class PersonComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
if (o1.getWeight() < o2.getWeight()) {
return -1;
} else if (o1.getWeight() > o2.getWeight()) {
return 1;
} else {
return 0;
}
}
}
Person类:在Person类中不用再实现compareTo()方法
public class Person{
//身高
private int height;
//体重
private int weight;
//省略其他代码
}

此时如果再对该Person类中添加一些其他属性,按照不同的规则进行排序,例如按照大长腿进行排序,也只需要添加相应的属性和比较器即可,无需修改已有代码,非常复合开闭原则。
上述需求的解决方式,其实就运用到了设计模式中的 策略模式 。
策略模式
定义
定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。
策略模式让算法可以独立于使用它的客户而变化。
适用场景
策略模式用于算法的自由切换和扩展,它对应于解决一个问题的一个算法族,
允许用户从该算法族中任选一个算法来解决某一问题,同时可以方便地更换
算法或者增加新的算法。只要涉及到算法的封装,服用和切换都可以考虑使用策略模式。
优点
- 提供了对开闭原则的完美支持,在不修改原有代码基础上即可灵活增加新的算法或者行为
- 提供了管理相关的算法族的办法,可以通过使用继承把不同策略中的公共代码抽象出来
- 提供了一种可以替换继承关系的办法。策略与环境分离开来,可以避免由于继承关系带来的复杂问题
- 使用策略模式可以避免多重条件选择语句
- 提供了一种算法复用机制,由于算法是单独地提取出来进行封装,因此不同的环境类可以方便地进行复用
缺点
- 客户端必须知道所有的策略类,并需要自行决定使用哪一种策略。因此该模式只适用于客户端知道所有策略的情况
- 策略模式会导致系统产生过多的具体策略类
- 无法同时在客户端使用多个策略类