简介
策略模式:策略模式可以定制目标对象的行为,它尅通过传入不同的策略实现,来配置目标对象的行为。使用策略模式,就是为了定制目标对象在某个关键点的行为。
策略模式中的角色:
- 上下文类:持有一个策略类的引用,最终给客户端调用
- 策略接口:定义规范,所有的策略类都要实现
- 具体策略累:实现具体算法
优缺点:
- 优点:
- 避免多重if else语句
- 策略类之间可以自由切换;
- 增加一个新的策略只需要添加一个具体的策略类即可;
- 缺点:客户端必须知道所有的策略类,并自行决定使用哪一个策略类
策略模式的实现
案例:售货员和优惠策略,用户可以配置售货员使用的优惠策略
第一步:上下文类
public class SaleMan {
// 上下文类持有策略接口的实例,用户通过传入策略接口的不同实现,来配置上下文类的行为
private final Strategy strategy;
public SaleMan() { }
public SaleMan(Strategy strategy) {
this.strategy = strategy;
}
public void saleManShow() {
System.out.print("售货员促销商品时使用的优惠策略:");
strategy.show();
}
}
第二步:策略接口
public interface Strategy {
void show();
}
第三步:具体的策略类
// 策略1
public class StrategyA implements Strategy {
@Override
public void show() {
System.out.println("买一送一");
}
}
// 策略2
public class StrategyB implements Strategy{
@Override
public void show() {
System.out.println("满200减50");
}
}
// 策略3
public class StrategyC implements Strategy{
@Override
public void show() {
System.out.println("满1000减200");
}
}
测试:
public class Client {
public static void main(String[] args) {
SaleMan saleMan = new SaleMan(new StrategyA());
saleMan.saleManShow();
}
}
总结:在这个案例中,用户在创建售货员实例时,可以配置售货员使用的优惠策略
策略模式本质上就是把上下文类中的某个关键流程提取出来,抽象出接口和实现类,接口就是关键流程要做什么,实现类就是关键流程的不同实现。如果不使用策略模式,这些策略都要放到上下文类中,那么用户需要传入参数来指定走哪条链路,这会导致大量的if else。策略模式可以让目标类更简洁。
使用案例
jdk源码案例:线程池的拒绝策略
1、上下文类
public class ThreadPoolExecutor extends AbstractExecutorService {
// 1、上下文类持有策略接口的实例
// 拒绝策略
private volatile RejectedExecutionHandler handler;
// 2、用户通过构造方法来指定上下文类使用哪个拒绝策略
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) { // 拒绝策略
// 这里省略了大量代码,重点关注,外部传入策略接口的实例,来指定线程池在无法
// 执行任务时该怎么做
this.handler = handler;
}
// 3、拒绝策略在上下文类中的执行。线程池执行任务的机制
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command); // 重点看这里,如果无法执行任务,就执行拒绝策略
}
// 执行拒绝策略
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
}
2、策略接口
public interface RejectedExecutionHandler {
// 策略方法,任务无法执行时线程池该怎么办
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
3、具体的策略实现
// 策略1:抛异常,这是默认的拒绝策略
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// 抛异常
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
// 策略2:如果线程池没有关闭,由调用者来执行任务
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
// 策略3:如果线程池没有关闭,丢弃队列中最老的任务
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll(); // 阻塞队列的头结点出队
e.execute(r); // 执行当前异步任务
}
}
}
// 策略4:丢失任务,静默,不抛异常
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
在学习线程池时,需要了解线程池的拒绝策略,并且选择一个合适的拒绝策略,这里就是原因,使用了策略模式,用户需要了解所有策略,并且指定使用哪种策略。