文章目录
策略模式(Strategy Pattern)
在策略模式(Strategy Pattern)行为型模式。一个类的行为或其算法可以在运行时更改。
这是一个让系统中尽可能减少if…else的模式
应用
使用模板(Java)
//环境类,即调用算法的类
class Context{
//保存算法对象
private final Strategy strategy;
public Context(Strategy strategy) { this.strategy = strategy; }
//调用算法对象
void operation(){ strategy.algorithm(); }
}
//算法抽象类
interface Strategy {
//算法方法接口,即被调用的接口方法
void algorithm();
}
//具体的抽象类A
class ConcreteStrategyA implements Strategy {
//具体实现
@Override
public void algorithm() {
System.out.println("算法A");
}
}
//同上
class ConcreteStrategyB implements Strategy {
@Override
public void algorithm() { System.out.println("算法B"); }
}
//同上
class ConcreteStrategyC implements Strategy {
@Override
public void algorithm() { System.out.println("算法C"); }
}
public class StrategyPattern {
public static void main(String[] args) {
//具体策略类
Strategy strategyA = new ConcreteStrategyA();
//环境类
Context contextA = new Context(strategyA);
//调用环境类
contextA.operation();
//同上
Strategy strategyB = new ConcreteStrategyB();
Context contextB = new Context(strategyB);
contextB.operation();
//同上
Strategy strategyC = new ConcreteStrategyC();
Context contextC = new Context(strategyC);
contextC.operation();
}
}
为什么说可以代替if...else
呢?
被替代的if…else写法
class BaseOperation {
public static void main(String[] args) {
new BaseOperation().algorithm("算法A");
new BaseOperation().algorithm("算法B");
new BaseOperation().algorithm("算法C");
}
public void algorithm(String type){
if ("算法A".equals(type)) {
System.out.println("A");
}else if ("算法B".equals(type)) {
System.out.println("B");
}else if("算法C".equals(type)){
System.out.println("C");
}
}
}
这样写的情况下
- 算法过多就需要在一个类中写无数个
if...else
,会造成代码冗余。 - 不符合开闭原则,无论更改或者增加算法,皆会导致代码的改动。
JDK中的使用
- 布局管理器
LayoutManager
- 线程池创建时的
RejectedExecutionHandler
简介
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。. 策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。. 用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。
UML
角色
- **环境类(Context):**调用该策略的类。
- 抽象策略类(Strategy): 定义需要的被调用策略类的抽象接口。
- **具体策略类(ConcreteStrategy):**具体的算法实现类,该类实现了抽象策略类。
意图:
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。
主要解决:
在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
何时使用:
一个系统有许多许多类,而区分它们的只是他们直接的行为(具体的算法)。
如何解决:
将这些算法单独封装成一个个的类,在使用时任意替换。
关键代码:
实现同一个接口。
优点:
-
算法自由切换。根据需要创建需要的具体策略类。
-
避免使用多重条件判断。根据条件创建对应的具体策略类来替代多重的条件判断。
-
优秀的扩展性。仅需继承或实现对应抽象策略类,即可直接使用。
缺点:
- 策略类会增多。
- 所有策略类都需要对外暴露。
使用场景:
- 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
- 一个系统需要动态地在几种算法中选择一种。
- 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
JDK使用分析
类RejectedExecutionHandler
UML
角色对应
- **环境类(Context):**类
ThreadPoolExecutor
,该类中使用了策略类。 - **抽象策略类(Strategy):**类
RejectedExecutionHandler
。 - 具体策略类(ConcreteStrategy): 类
CallerBlocksPolicy
、AbortPolicy
等,这些类都实现了RejectedExecutionHandler
(抽象策略类)
代码分析
JDK中使用Executors
创建线程池实际上是调用了类ThreadPoolExecutor
的构造器(构造线程池)。即环境类。
public class ThreadPoolExecutor extends AbstractExecutorService {
...
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;
}
...
}
关于线程池创建不详述,重点在于其中参数handler(RejectedExecutionHandler
)。即抽象策略类。
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
而以下两个类CallerBlocksPolicy
、AbortPolicy
为上述接口的具体实现类。即具体策略类。
**(*){...
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 class CallerBlocksPolicy implements RejectedExecutionHandler {
private static final Log LOGGER = LogFactory.getLog(CallerBlocksPolicy.class);
private final long maxWait;
/**
* Construct instance based on the provided maximum wait time.
* @param maxWait The maximum time to wait for a queue slot to be available, in milliseconds.
*/
public CallerBlocksPolicy(long maxWait) {
this.maxWait = maxWait;
}
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (!executor.isShutdown()) {
try {
BlockingQueue<Runnable> queue = executor.getQueue();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Attempting to queue task execution for " + this.maxWait + " milliseconds");
}
if (!queue.offer(r, this.maxWait, TimeUnit.MILLISECONDS)) {
throw new RejectedExecutionException("Max wait time expired to queue task");
}
LOGGER.debug("Task execution queued");
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RejectedExecutionException("Interrupted", e);
}
}
else {
throw new RejectedExecutionException("Executor has been shut down");
}
}
}