1 概念
策略模式也叫政策模式,是一种比较简单的模式,其定义是
定义一组算法,将每个算法都封装起来,并且使它们之间可以互换
2 概念理解
策略模式使用的是面向对象的继承和多态机制,将算法实现从业务逻辑中剥离出来成为一系列独立的算法类,每个算法实现相同的接口就可以实现相互转换。
策略模式通用类图:
-
Context封装角色
上下文角色,一般持有策略的引用,进行对策略的调用,具体的策略对象也可以从上下文角色中获取所需要的数据,可以将上下文当做参数传入到具体的参数中,具体策略通过回调上下文来获取所需要的数据。
-
Strategy抽象策略角色
策略、算法家族的抽象,通常为接口,定义每个策略或者算法必须具有的方法和属性。
-
ConcreteStrategy具体策略角色
实现抽象策略中的操作,该类含有具体的算法。
3 应用场景
- 多个类存在算法和行为上类似的场景
- 算法自由切换的场景
- 屏蔽算法规则的场景
4 优缺点
4.1 策略模式优点
-
算法可以自由切换
-
拓展性良好
-
避免使用多重条件判断
使用了策略模式后,可以由其他模块决定采用何种策略,策略对外提供的访问接口就是封装类。
4.2 策略模式的缺点
-
策略类数量增多
每个策略都是一个类,复用可能性小,类数量增多
-
所有策略对外暴露
上层模块需要知道哪些策略选择取调用哪个策略,这违背了迪米特法则,不过这个可以通过工厂模式方法和代理模式或者享元模式来修正这个缺陷。
5 参考例子
设计模式在Spring中得到近乎完美的体现,以策略模式在Spring应用的一个例子做讲解。
在多线程编程中,我们经常使用线程池来管理线程,以减缓线程频繁的创建和销毁带来的资源的浪费,在创建线程池的时候,经常使用一个工厂类来创建线程池Executors,实际上Executors的内部使用的是ThreadPoolExecutor.其中有一个的构造函数如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
类ThreadPoolExecutor中持有一个RejectedExecutionHandler接口的引用,以便在构造函数中可以由外部客户端自己制定具体的策略并注入。
RejectedExecutionHandler 是一个策略接口,用在当线程池中没有多余的线程来执行任务,并且保存任务的队列也满了(指的是有界队列),对仍在提交给线程池的任务的处理策略。
public interface RejectedExecutionHandler {
/**
*当ThreadPoolExecutor的execut方法调用时,并且ThreadPoolExecutor不能接受一个任务Task时,该方法就有可能被调用。
* 不能接受一个任务Task的原因:有可能是没有多余的线程来处理,有可能是workqueue队列中没有多余的位置来存放该任务,该方法有可能抛出一个未受检的异常RejectedExecutionException
* @param r the runnable task requested to be executed
* @param executor the executor attempting to execute this task
* @throws RejectedExecutionException if there is no remedy
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
该策略接口有四个实现类:
AbortPolicy:该策略是直接将提交的任务抛弃掉,并抛出RejectedExecutionException异常。
/**
* A handler for rejected tasks that throws a
* <tt>RejectedExecutionException</tt>.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an <tt>AbortPolicy</tt>.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always.
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException();
}
}
DiscardPolicy: 该策略也是将任务抛弃掉,但并不抛出异常。
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a <tt>DiscardPolicy</tt>.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
DiscardOldestPolicy:该策略是当执行器未关闭时,从任务队列workQueue中取出第一个任务,并抛弃这第一个任务,进而有空间存储刚刚提交的任务。使用该策略要特别小心,因为它会直接抛弃之前的任务。
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries <tt>execute</tt>, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a <tt>DiscardOldestPolicy</tt> for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
CallerRunsPolicy:该策略并没有抛弃任何的任务,由于线程池中已经没有了多余的线程来分配该任务,该策略是在当前线程(调用者线程)中直接执行该任务。
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
总结:
策略模式体现了开闭原则:策略模式把一系列的可变算法进行封装,从而定义了良好的程序结构,在出现新的算法的时候,可以很容易的将新的算法实现加入到已有的系统中,而已有的实现不需要修改。
策略模式体现了里氏替换原则:策略模式是一个扁平的结构,各个策略实现都是兄弟关系,实现了同一个接口或者继承了同一个抽象类。这样只要使用策略的客户端保持面向抽象编程,就可以动态的切换不同的策略实现以进行替换。
简言之,策略模式的本质是分离算法,选择实现
才疏浅薄,有误望指正