概述
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
策略模式(Strategy Design Pattern)定义了一系列算法,将每个算法分别封装起来,并使它们可以相互替换。
策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。 策略模式只适用管理一组同类型的算法,并且这些算法是完全互斥的情况。
实现
- Strategy(抽象策略):特定策略的抽象
- ConcreteStrategy(具体策略):实现抽象策略的类
- Context(环境):运行特定策略的类
策略模式的通用写法:
func main() {
// 选择一个具体策略
strategy := new(concreteStrategyA)
// 创建一个上下文环境
context := NewContext(strategy)
// 客户端直接让上下文角色执行算法
context.do()
}
// IStrategy 抽象策略
type IStrategy interface {
algorithm()
}
// concreteStrategyA 具体策略
type concreteStrategyA struct{}
func (*concreteStrategyA) algorithm() {
fmt.Println("Strategy A")
}
// concreteStrategyB 具体策略
type concreteStrategyB struct{}
func (*concreteStrategyB) algorithm() {
fmt.Println("Strategy B")
}
// Context 上下文环境
type Context struct {
strategy IStrategy
}
func NewContext(strategy IStrategy) *Context {
return &Context{
strategy: strategy,
}
}
// 调用策略中的方法
func (context *Context) do() {
context.strategy.algorithm()
}
应用
策略模式在JDK中的应用:
// 处理不能被 ThreadPoolExecutor 执行的 tasks
// 该策略接口有四个实现类
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);
}
/* Predefined RejectedExecutionHandlers */
/**
*
* 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();
}
}
}
/**
*
* A handler for rejected tasks that throws a
* {@link RejectedExecutionException}.
*
*
* This is the default handler for {@link ThreadPoolExecutor} and
*
* {@link ScheduledThreadPoolExecutor}.
*
*/
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());
}
}
/**
*
* A handler for rejected tasks that silently discards the
* rejected task.
*/
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) {
}
}
/**
*
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
*
* is shut down, in which case the task is discarded. This policy is
* rarely useful in cases where other threads may be waiting for
* tasks to terminate, or failures must be recorded. Instead consider
* using a handler of the form:
*
* <pre> {@code
*
* new RejectedExecutionHandler() {
*
* public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
* Runnable dropped = e.getQueue().poll();
* if (dropped instanceof Future<?>) {
*
* ((Future<?>) dropped).cancel(false);
*
* // also consider logging the failure
* }
* e.execute(r); // retry
* }
* }
* }</pre>
*
*/
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);
}
}
}
总结
策略模式的优点:
- 策略模式符合开闭原则
- 避免使用多重条件转移语句,如if...else语句、switch...case预警
- 使用策略模式可以提高算法的保密性和安全行
策略模式的缺点:
- 客户端必须知道所有的策略,并且自行决定使用哪一个策略类;在实际项目中,一般通过工厂方法模式来实现策略类的声明
- 代码中会产生非常多策略类,增加维护难度
使用策略工厂模式来管理策略类:
func main() {
factory := NewStrategyFactory()
if strategy, ok := factory.GetStrategy("B"); ok {
strategy.algorithm()
}
}
// 策略工厂
type StrategyFactory struct {
strategys map[string]IStrategy
}
func NewStrategyFactory() *StrategyFactory {
factory := new(StrategyFactory)
strategys := map[string]IStrategy{
"A": new(concreteStrategyA),
"B": new(concreteStrategyB),
}
factory.strategys = strategys
return factory
}
func (factory *StrategyFactory) GetStrategy(name string) (strategy IStrategy, ok bool) {
if v, ok := factory.strategys[name]; ok {
return v, true
}
return nil, false
}
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。