策略模式(Strategy):
它定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法的变化不会影响到使用算法的客户。(原文:The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.)
动机:
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?
策略模式就能使算法与对象本身解耦,能够在需要时透明的更改对象的算法。
通俗点讲,策略模式就是将对象本身的算法进行抽象,从而从对象本身中剥离出来。
比如对某一类对象obj进行比较,我们可能会采取多种比较策略,如果每一种比较策略都在对象obj中写一个实现方法,这个类将会膨胀。
在我们看来,所有的比较归根结底就是比较出两个对象的大小,所以我们可以对这一部分进行抽象,抽成一个comparator比较器,由外部调用处对这个比较器进行注入。这样我们就能灵活的控制到底使用哪一种比较规则了。
举例:
比较学生,1:按年龄比较 2:按身高比较
public interface Comparator<T> { public int compare(T obj1, T obj2); }
public class Student { private String name; private int age; private double height; private Comparator<Student> comparator; public Comparator<Student> getComparator() { return comparator; } public void setComparator(Comparator<Student> comparator) { this.comparator = comparator; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int compare(Student s) { return this.comparator.compare(this, s); } }
public class StuAgeComparator implements Comparator<Student> { @Override public int compare(Student s1, Student s2) { if(s1.getAge() > s2.getAge()){ return 1; } else if (s1.getAge() == s2.getAge()){ return 0; } else { return -1; } } }
public class StuHeightComparator implements Comparator<Student> { @Override public int compare(Student s1, Student s2) { if(s1.getHeight() > s2.getHeight()){ return 1; } else if (s1.getHeight() == s2.getHeight()){ return 0; } else { return -1; } } }
客户端调用:
public class Client { public static void main(String[] args) { Student s1 = new Student(); s1.setName("张三"); s1.setAge(18); s1.setHeight(1.7); Student s2 = new Student(); s2.setName("李四"); s2.setAge(15); s2.setHeight(1.80); s1.setComparator(new StuAgeComparator()); System.out.println(s1.compare(s2)); s1.setComparator(new StuHeightComparator()); System.out.println(s1.compare(s2)); } }
其实我上面讲的就是JDK中的Comparator这个接口,但是好像还少了点什么,少的就是Comparable这个接口。
这个接口是为通用的排序做准备的。
我们知道Conllections.sort(List<T> args)这个方法是对List中的对象进行排序,不管它里面装的是什么对象。那么我们想对任意的对象进行排序的话,就不能用具体的排序的实现方法,也就是要对排序进行抽象,而排序中重要的一环就是比较两个对象的大小,所以归根结底就是对比较大小进行抽象,使用子类的实现。
所以我们就考虑让所有类型的对象都实现统一的接口,对象比较时都使用这个接口的比较方法,故最终比较时就会使用子类的比较方法的实现来进行大小比较。
例:
public interface Comparable<T> { public int compareTo(T obj); }
public class Student implements Comparable<Student>{ private String name; private int age; private double height; private Comparator<Student> comparator = new StuAgeComparator(); // getter/setter方法 public int compareTo(Student s) { return this.comparator.compare(this, s); } }
客户端调用代码:
public class ClientSort { public static void main(String[] args) { Student s1 = new Student(); s1.setName("张三"); s1.setAge(18); s1.setHeight(1.7); Student s2 = new Student(); s2.setName("李四"); s2.setAge(15); s2.setHeight(1.80); Student s3 = new Student(); s3.setName("王五"); s3.setAge(21); s3.setHeight(1.65); Comparable<Student>[] stus = new Student[3]; stus[0] = s1; stus[1] = s2; stus[2] = s3; stus = DataSorter.sort(stus); for(Comparable<Student> s : stus){ System.out.println(((Student)s).getName() + ":" + ((Student)s).getAge()); } } }
输出:
李四:15
张三:18
王五:21
所以,Comparable这个接口与Strategy策略模式是不相关的,它主要的作用是统一比较接口。
Comparator的实现才是具体的比较策略。