定义
策略模式(Strategy):它定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法的变化不会影响到使用算法的客户。
角色
- Strategy接口:定义了算法的形式;
- ConcreteStrategy:实现了具体算法;
- Context:对接口中的算法进行实用;
- Client:测试类;
实际使用
这里我们可以看个例子:
例子一:
我们有一个这样的类:
package com.freesoft.designpattern.strategy;
public class Person {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
有一天我们想对这个类组成的集合对象进行排序,那么必须要有一个手段能够对这个类的对象进行比较,所以我们这样实现:
package com.freesoft.designpattern.strategy;
public class Person implements Comparable<Person> {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Person o) {
if (this.getId() != o.getId()) {
Integer id1 = Integer.valueOf(this.getId());
Integer id2 = Integer.valueOf(o.getId());
return id1.compareTo(id2);
} else {
String name1 = this.getName();
String name2 = o.getName();
return name1.compareTo(name2);
}
}
}
通过以上实现,我们能够对Person类型的两个对象实现比较操作,首先比较id,如果id相同那么比较name字段。在集合中我们可以根据这个规则(策略)来对集合进行排序。
问题来了,我们不能保证这个规则/策略就是我们需要的策略。比如,我们可能需要先比较name字段,或者我们可能在某种情况下使用id优先原则,某种情况下使用name优先原则,所以我们的策略是会变化的。
考虑到以上问题,我们需要修改我们的代码,我们单独编写一个策略类:
package com.freesoft.designpattern.strategy;
import java.util.Comparator;
public class PersonComparatorById implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
if (o1.getId() != o2.getId()) {
Integer id1 = Integer.valueOf(o1.getId());
Integer id2 = Integer.valueOf(o2.getId());
return id1.compareTo(id2);
} else {
String name1 = o1.getName();
String name2 = o2.getName();
return name1.compareTo(name2);
}
}
}
并且我们的Person代码变为:
package com.freesoft.designpattern.strategy;
import java.util.Comparator;
public class Person implements Comparable<Person> {
private int id;
private String name;
private Comparator<Person> c;
public Person(int id, String name) {
super();
this.id = id;
this.name = name;
}
public Comparator<Person> getC() {
return c;
}
public void setC(Comparator<Person> c) {
this.c = c;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Person o) {
return c.compare(this, o);
}
}
这样如果我们今后需要使用name优先的比较原则,我们只需要实现一个新类:
package com.freesoft.designpattern.strategy;
import java.util.Comparator;
public class PersonComparatorByName implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
if (o1.getName().equals(o2.getName())) {
Integer id1 = Integer.valueOf(o1.getId());
Integer id2 = Integer.valueOf(o2.getId());
return id1.compareTo(id2);
} else {
String name1 = o1.getName();
String name2 = o2.getName();
return name1.compareTo(name2);
}
}
}
我们的测试类如下:
package com.freesoft.designpattern.strategy;
public class Client {
public static void main(String[] args) {
Person p1 = new Person(10, "zhangsan");
Person p2 = new Person(20, "lisi");
PersonComparatorById cId = new PersonComparatorById();
PersonComparatorByName cName = new PersonComparatorByName();
p1.setC(cId);
System.out.println("p1 compara p2 by Id: " + p1.compareTo(p2));
p1.setC(cName);
System.out.println("p1 compara p2 by Name: " + p1.compareTo(p2));
}
}
总结
- 我们的Comparator接口就是我们抽象的策略;
- 我们实现的一个个的PersonComparatorByXXX就是具体的策略;
- Person提供了比较的功能,但实际使用时通过setC传入不同的比较策略,所以在实际使用中非常灵活,并且可以独立单元测试、代码维护成本极低。