1、定义
策略模式定义了算法族,分别封装起来,让他们之间可以相互替代,此模式的变化独立于使用算法的客户
上述中的算法即行为、方法,策略模式通俗点说,解决一个问题有多种方法,程序在运行时会自己去选择执行什么方法
策略模式的角色:
- 抽象策略角色(Strategy):策略类,通常由一个接口或者抽象类实现
- 具体策略角色(ConcreteStrategy):包装了相关的算法和行为
- 环境角色(Context):持有一个策略类的引用,最终给客户端调用
2、例子
HeadFirst上面的例子简化版:鸭子能飞,但是每种特定的鸭子飞的行为不一样,这里不一样的是飞这个行为
抽象策略类:
public interface FlyBehavior {
void fly();
}
具体策略类1:
public class FlyWithWings implements FlyBehavior{
@Override
public void fly() {
System.out.println("用翅膀飞");
}
}
具体策略类2:
public class FlyWithRocket implements FlyBehavior {
@Override
public void fly() {
System.out.println("用火箭飞");
}
}
具体策略类3:
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("不能飞");
}
}
抽象环境角色类:
public abstract class Duck {
protected FlyBehavior flyBehavior;
public void PerformFlyBehavior(){
display();
flyBehavior.fly();
}
abstract void display();
}
具体环境角色类1:
public class RedHeadDuck extends Duck {
public RedHeadDuck(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
@Override
void display() {
System.out.print("红头鸭,");
}
}
具体环境角色类2:
public class MechanicalDuck extends Duck {
public MechanicalDuck(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
@Override
void display() {
System.out.print("机械鸭,");
}
}
具体环境角色类3:
public class SimulatorDuck extends Duck {
public SimulatorDuck(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
@Override
void display() {
System.out.print("模型鸭,");
}
}
客户,也就是测试:
public class Test {
public static void main(String[] args) {
FlyWithWings wings = new FlyWithWings();
FlyWithRocket rocket = new FlyWithRocket();
FlyNoWay noway = new FlyNoWay();
Duck[] ducks = {
new RedHeadDuck(wings),
new MechanicalDuck(rocket),
new SimulatorDuck(noway),
};
for (Duck duck : ducks) {
duck.PerformFlyBehavior();
}
}
}
输出:
红头鸭,用翅膀飞
机械鸭,用火箭飞
模型鸭,不能飞
3、优缺点和应用场景
3.1优点
- 策略封装比较彻底,也比较安全,用户可以调用策略,但是不知道策略是如何实现的,也不能更改策略
- 扩展性好,如果解决方法未来可能会增加,可以继续扩展,不用更改原有逻辑代码
- 摆脱臃肿,避免选择解决方法时使用大量if-else或者switch-case
3.2缺点
- 会带来类数量上的增加,可以使用工厂方法来解决
- 客户端必须清楚得知道所有的策略,至少得知道能够调用的策略是什么、有多少
3.3应用场景
- 针对一个问题有多种解决方法的时候,用来选择具体的哪种方法。
- 具体的实现方法可以进行自由的切换。
4、使用到的面向对象原则
- 面向接口,而不是实现
- 开放-封闭原则
- 依赖倒置原则
5、在JDK中的应用
4.1 java.util.Comparator#compare()
强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。
以上摘自jdk文档中对Comparator的描述,准确地说是Collections.sort(List list, Comparator
public interface Comparator<T>{
int compare(T o1, T o2);
boolean equals(Object obj);
……
}
啥意思呢?以Arrays.sort()为例, static<T> void sort(T[] a, Comparator<? super T> c)
根据指定比较器产生的顺序对指定对象数组进行排序。
这里compare方法即是策略,Comparator是抽象策略类,而实现Comparator的类为具体策略类,对于每个不同的对象来说,实现比较器的策略是不一样的,以下为一个简单的例子。
具体策略类:
public class PersonComparator implements Comparator<Person>{
@Override
public int compare(Person p1, Person p2) {
if(p1.getAge() > p2.getAge())
return 1;
else if(p1.getAge() < p2.getAge())
return -1;
else
return 0;
}
}
Person类:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "name: "+name+", age: "+age;
}
}
测试类:
import java.util.*;
public class ComparatorTest {
public static void main(String[] args) {
Person[] persons = {
new Person("张三", 20),
new Person("李四", 50),
new Person("王五", 30),
};
for (Person person : persons) {
System.out.print(person+" ");
}
//升序排序
Arrays.sort(persons, new PersonComparator());
System.out.println(Arrays.toString(persons));
}
}
输出:
[name: 张三, age: 20, name: 李四, age: 50, name: 王五, age: 30]
[name: 张三, age: 20, name: 王五, age: 30, name: 李四, age: 50]
这里还有另外一个知识点,ComparableComparator和联系:Cmparable是排序接口,一个类实现类该接口表明“该类支持排序“——可使用Collections.sort(或 Arrays.sort)进行排序,可理解为”内部比较器”;Comparator是比较器接口,一个类本不”支持排序“——无法使用Collections.sort(或 Arrays.sort)进行排序,也就是没有实现Comparable接口,可以额外创建一个比较器类来实现“排序”,可以理解为“外部比较器”
4.2 javax.servlet.http.HttpServlet
HTTP请求实际上只是一个HTTP请求报文,Web容器会自动将这个HTTP请求报文包装成一个HttpServletRequest对象,并且自动调用HttpServlet的 service() 方法来解析这个HTTP请求,service()方法会解析HTTP请求行,而HTTP请求行由method,URI,HTTPVersion三个组成,method就是get或者post,service() 方法根据 method 来决定是执行 doGet 还是 doPost,这一切都是服务器(容器,比如tomcat)自动完成的,HTTP的格式也自动被解析。只要自定义的类继承了HttpServlet,并且在web.xml里面配置了相应的servlet和mapping,服务器就会自动执行以上过程。
而自定义servlet必须实现Servlet接口,一般重写doGet()或doPost(),HttpServlet即是抽象策略类,自定以servlet即具体实现类
另外,线程池ThreadPoolExecutor实现和ForkJoin框架的ForkJoinPool实现也应用到了策略模式,这里先放着,因为属于并发的内容
参考资料;
- HeadFirst设计模式
- http://blog.csdn.net/lovelion/article/details/8765177
- http://blog.csdn.net/ydxlt/article/details/50434341
- http://blog.csdn.net/u013256816/article/details/51245046
- http://blog.csdn.net/wangyang1354/article/details/51647076
- http://www.cnblogs.com/kubixuesheng/p/5155644.html
- http://www.importnew.com/12853.html