什么是策略模式
策略模式定义了一系列算法类,将每一个算法封装起来,并让他们可以相互替换,策略模式让算法独立于客户端。
策略模式主要包含以下几个角色:
Context(环境类):使用算法的角色,在解决某个问题(即实现某个方法)时可以采用多种策略,在环境类中维持一个抽象策略类的引用实例,用于定义所采用的策略。
Strategy(抽象策略类):为所支持的算法声明抽象方法,是所有策略类的父类,可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的方法。
ConcreteStrategy(具体策略类):实现了抽象策略类中声明的方法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象。
策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开,将算法的定义放在专门的策略类中,每一个策略类封装了一种实现的算法,使用算法的环境类针对抽象策略类进行编程,符合依赖倒转原则。
策略模式的优缺点
优点
- 用户可以在不修改原代码的基础上选择算法或行为,也可以灵活的增加新的算法,符合开闭原则。
- 提供了管理相关的算法族的方法。
- 提供了一种可以替换继承关系的方法。
- 避免了多重条件选择语句。
- 提供了算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便的复用。
缺点
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
- 系统可能会产生很多具体策略类。
- 无法同时使用多个策略类,每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类完成剩余功能。
策略模式的应用场景
- 一个系统需要动态的在几种算法中选择一种。
- 一个对象有很多行为,并且这些行为是使用多重条件语句来实现。
- 不希望客户端知道与算法相关的数据结构,在具体策略类中封装算法相关的数据结构。
策略模式的案例
// 抽象策略类
public interface FlyBehavior {
void fly(); // 子类具体实现
}
// 具体策略类
public class BadFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println(" 飞翔技术一般 ");
}
}
public class GoodFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println(" 飞翔技术高超 ~~~");
}
}
public class NoFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println(" 不会飞翔 ");
}
}
public static void main(String[] args) {
WildDuck wildDuck = new WildDuck();
wildDuck.fly();//
ToyDuck toyDuck = new ToyDuck();
toyDuck.fly();
PekingDuck pekingDuck = new PekingDuck();
pekingDuck.fly();
//动态改变某个对象的行为, 北京鸭 不能飞
pekingDuck.setFlyBehavior(new NoFlyBehavior());
System.out.println("北京鸭的实际飞翔能力");
pekingDuck.fly();
}
策略模式在源码中的应用
RejectedExecutionHandler
// 策略接口
public interface RejectedExecutionHandler {
/**
* Method that may be invoked by a {@link ThreadPoolExecutor} when
* {@link ThreadPoolExecutor#execute execute} cannot accept a
* task. This may occur when no more threads or queue slots are
* available because their bounds would be exceeded, or upon
* shutdown of the Executor.
*
* <p>In the absence of other alternatives, the method may throw
* an unchecked {@link RejectedExecutionException}, which will be
* propagated to the caller of {@code execute}.
*
* @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);
}
// 具体策略类
public class ThreadPoolExecutor extends AbstractExecutorService {
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();
}
}
}
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
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("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
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) {
}
}
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} 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);
}
}
}
......
}