设计模式之策略模式
一、策略模式概述
策略模式(Strategy):定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的调用者。
二、策略模式解析
策略模式是一种定义一系列算法的方法,从概念上看,所有的算法完成的都是相同的工作,只是实现不同,策略模式可以用相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
策略模式的Strategy类层次结构为Context(上下文)定义了一系列可供重用的算法或行为,继承有助于析取出这些算法的公共功能。
三、策略模式UML类图
四、代码示例
没有代码的陪衬就好像在耍流氓,下面来看策略模式的代码示例。
场景:
就拿去公司上班的场景为例,我们上班必须要去公司,这个目的地是固定的。员工怎么去到公司呢,根据实际场景,住的离公司近的同事,可以选择步行或者骑车,住的远的可能会选择自驾,出行的方式由员工而定。下面我们用代码来实现这一场景。
4.1、将出行方式抽象

1 /** 2 * 出行至公司的方式 3 */ 4 public interface ITripMode { 5 6 public String tripWay(); 7 8 }
4.2、出行方式选择步行

1 public class WalkStrategy implements ITripMode { 2 @Override 3 public String tripWay() { 4 return "步行至公司"; 5 } 6 }
4.3、出行方式选择自驾

1 public class CarTripStrategy implements ITripMode { 2 @Override 3 public String tripWay() { 4 return "自驾至公司"; 5 } 6 }
4.4、出行方式选择骑行

1 public class BikeStrategy implements ITripMode { 2 @Override 3 public String tripWay() { 4 return "骑行至公司"; 5 } 6 }
4.5、持有出行方式(算法)的上下文

1 public class TripModeContext { 2 3 private ITripMode mode; 4 5 public TripModeContext(ITripMode mode) { 6 this.mode = mode; 7 } 8 9 public String tripWay() { 10 return mode.tripWay(); 11 } 12 13 }
4.6、测试

1 public class StrategyTest { 2 public static void main(String[] args) { 3 ITripMode mode = new CarTripStrategy(); 4 TripModeContext context = new TripModeContext(mode); 5 System.out.println("选择的出行方式是:" + context.tripWay()); 6 } 7 }
运行结果如下:
ITripMode定义了算法的公共接口;BikeStrategy、WalkStrategy、CarStrategy作为具体的算法实现;TripModeContext作为上下文,持有ITripMode的引用,作为算法处理的入口,由此可见,策略模式将具体的算法实现与调用算法的客户端做了解耦,当员工有其他的出行方式时,创建一个类实现ITripMode,不必修改现有代码逻辑,使得系统更容易维护。
五、策略模式在JDK中的应用
线程池中的拒绝策略采用了策略模式,分析如下:
5.1、我们先来看拒绝策略的老大哥RejectedExecutionHandler,老大哥规定了拒绝的算法。

1 public interface RejectedExecutionHandler { 2 void rejectedExecution(Runnable r, ThreadPoolExecutor executor); 3 }
5.2、abort小弟实现RejectedExecutionHandler的方式

1 //ThreadPoolExecutor中的静态内部类 2 public static class AbortPolicy implements RejectedExecutionHandler { 3 public AbortPolicy() { } 4 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 5 throw new RejectedExecutionException("Task " + r.toString() + 6 " rejected from " + 7 e.toString()); 8 } 9 }
abort小弟实现的拒绝策略:一旦指定拒绝策略为AbortPolicy ,任务数量使线程池饱和时,会抛出异常。
5.3、discardOldest小弟实现RejectedExecutionHandler的方式

1 public static class DiscardOldestPolicy implements RejectedExecutionHandler { 2 public DiscardOldestPolicy() { } 3 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 4 if (!e.isShutdown()) { 5 e.getQueue().poll(); 6 e.execute(r); 7 } 8 } 9 }
discardOldest小弟实现的拒绝策略:当e.getQueue()获取线程的工作队列,因为队列遵循(FIFO)先进先出,所以当任务数量使线程池饱和时,线程池e.getQueue().poll()将队列头的数据移除,即移除工作队列中最老的任务,执行新的任务。
5.4、discard小弟实现RejectedExecutionHandler的方式

1 public static class DiscardPolicy implements RejectedExecutionHandler { 2 public DiscardPolicy() { } 3 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 4 } 5 }
discard小弟实现的拒绝策略:这个小弟最懒,什么都不做,直接把任务丢弃了。
5.5、CallerRuns小弟实现RejectedExecutionHandler的方式

1 public static class CallerRunsPolicy implements RejectedExecutionHandler { 2 public CallerRunsPolicy() { } 3 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 4 if (!e.isShutdown()) { 5 r.run(); 6 } 7 } 8 }
CallerRunsPolicy 小弟实现的拒绝策略:只要线程池没有关闭的话,则使用调用线程直接运行任务。
5.6、我们再来看看RejectedExecutionHandler 的上下文Context---ThreadPoolExecutor,主要代码如下:

1 public class ThreadPoolExecutor extends AbstractExecutorService { 2 ...... 3 4 /** 5 * Handler called when saturated or shutdown in execute. 6 */ 7 // 只有工作队列饱和或者线程关闭时调用拒绝的处理 8 private volatile RejectedExecutionHandler handler; 9 10 ...... 11 12 /** 13 * The default rejected execution handler 14 */ 15 // 线程池默认的拒绝策略 16 private static final RejectedExecutionHandler defaultHandler = new AbortPolicy(); 17 18 ...... 19 20 // ThreadPoolExecutor的构造函数,当不指定拒绝策略时, 21 //会调用默认的拒绝策略 22 public ThreadPoolExecutor(int corePoolSize, 23 int maximumPoolSize, 24 long keepAliveTime, 25 TimeUnit unit, 26 BlockingQueue<Runnable> workQueue) { 27 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, 28 Executors.defaultThreadFactory(), defaultHandler); 29 } 30 31 ...... 32 33 // ThreadPoolExecutor的构造函数,根据业务场景指定拒绝策略, 34 // 也可自定义拒绝策略 35 public ThreadPoolExecutor(int corePoolSize, 36 int maximumPoolSize, 37 long keepAliveTime, 38 TimeUnit unit, 39 BlockingQueue<Runnable> workQueue, 40 ThreadFactory threadFactory, 41 RejectedExecutionHandler handler) { 42 if (corePoolSize < 0 || 43 maximumPoolSize <= 0 || 44 maximumPoolSize < corePoolSize || 45 keepAliveTime < 0) 46 throw new IllegalArgumentException(); 47 if (workQueue == null || threadFactory == null || handler == null) 48 throw new NullPointerException(); 49 this.acc = System.getSecurityManager() == null ? 50 null : 51 AccessController.getContext(); 52 this.corePoolSize = corePoolSize; 53 this.maximumPoolSize = maximumPoolSize; 54 this.workQueue = workQueue; 55 this.keepAliveTime = unit.toNanos(keepAliveTime); 56 this.threadFactory = threadFactory; 57 this.handler = handler; 58 } 59 60 ...... 61 62 // 拒绝策略处理 63 final void reject(Runnable command) { 64 handler.rejectedExecution(command, this); 65 } 66 67 }
由上面的分析,我们可以做一下对比,
RejectedExecutionHandler作为策略模式中Strategy的存在,是对拒绝策略算法的抽象。
ThreadPoolExecutor 是作为策略模式中上下文的存在,客户端调用拒绝策略的入口。
本文部分内容摘自《大话设计模式》。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)