定义
定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的用户。即把不同的算法封装到不同的类里面,让它们之间可以相互替换, 应用层不会受到影响。
类型
行为型
使用场景
1 2 3 4
| 1 系统有很多类,而他们的区别仅仅在于它们的行为不同 - 使用策略模式就可以动态地让一个对象在多个行为中选择一种行为,也就是说我们把这个对象不同的行为放到不同的类里面,而每一种行为对应着一种策略 2 一个系统需要动态地在几种算法中选择一种 - 这里算法就是策略,策略里面封装的就是一系列逻辑以及计算方式
|
优点
1 2 3 4 5 6
| 1 符合开闭原则 - 策略模式提供了对开闭原则的完美支持,我们可以在不修改原有系统的基础上选择具体的行为 2 避免使用多重条件转移语句 - 大量的if...else, switch。 我们把具体的策略行为分离为一个一个的单独的类来替换if...else里面的逻辑,这样写也可以降低代码的耦合 3 提高算法的保密性和安全性 - 在使用的时候我们只知道策略的功能,不需要知道具体的细节。在具体的策略类中封装了不同的行为和算法以及相关的数据结构,对于应用层来说,是不需要知道内部的细节的。比如使用Dubbo的服务提供者,不需要知道内部逻辑的细节。
|
缺点
1 2
| 1 应用层必须知道所有的策略类,并自行决定使用哪一个策略类 2 产生很多策略类
|
策略模式相关的设计模式
策略模式和工厂模式
1 2
| 1 工厂模式是创建型的设计模式,策略模式是行为型的设计模式 2 工厂模式接受指令,创建符合要求的对象。策略模式接受创建好的对象,从而实现不同的行为
|
策略模式和状态模式
1 2
| 1 使用策略模式的时候,应用层需要知道应该选择哪一种策略。在使用状态模式的时候,应用层是不需要关心具体的状态,这些状态会自动转换 2 如果系统中某个类的对象存在多种状态,不同状态下行为又有差异,而且这些状态可以发生转换时可以使用状态模式。如果系统中某个类的某种行为存在多种实现方式,如促销是个行为,这种行为就有多种实现方式,这种情况下应该使用策略模式。
|
简单需求
当当网在双十一或者618的时候会有很多的促销活动,促销是书籍的一个行为,这个促销行为有多种实现。
策略模式演练
最基本的使用

策略父类型(这里是接口的方式)
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.design.pattern.strategy.base;
public interface PromotionStrategy {
void doPromotion(); }
|
具体策略实例
发现策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.design.pattern.strategy.base;
import lombok.extern.slf4j.Slf4j;
@Slf4j public class FanXianPromotionStrategy implements PromotionStrategy {
@Override public void doPromotion() { log.info("返现促销,返回的金额存放到账号的余额中"); } }
|
立减优惠策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.design.pattern.strategy.base;
import lombok.extern.slf4j.Slf4j;
@Slf4j public class LiJianPromotionStrategy implements PromotionStrategy {
@Override public void doPromotion() { log.info("立减促销,书籍的价格直接减去立减活动设置的价格"); } }
|
满减策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.design.pattern.strategy.base;
import lombok.extern.slf4j.Slf4j;
@Slf4j public class ManJianPromotionStrategy implements PromotionStrategy {
@Override public void doPromotion() { log.info("满减促销,满200减50"); } }
|
策略包装类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.design.pattern.strategy.base;
public class PromotionActivity {
private PromotionStrategy promotionStrategy;
public PromotionActivity(PromotionStrategy promotionStrategy){ this.promotionStrategy = promotionStrategy; }
public void execute(){ promotionStrategy.doPromotion(); } }
|
应用层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.design.pattern.strategy.base;
import com.design.pattern.strategy.base.FanXianPromotionStrategy; import com.design.pattern.strategy.base.ManJianPromotionStrategy; import com.design.pattern.strategy.base.PromotionActivity; import org.junit.Test;
public class Client {
@Test public void test(){
PromotionActivity activity618 = new PromotionActivity(new ManJianPromotionStrategy());
PromotionActivity activity11 = new PromotionActivity(new FanXianPromotionStrategy());
activity618.execute(); activity11.execute(); } }
|
小结
1
| 这是策略模式的简单使用,整体上扩展性比较好,想增加策略很方便。
|
基本使用演进
应用层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package com.design.pattern.strategy.v1;
import org.junit.Test; import org.springframework.util.ObjectUtils;
public class Client {
@Test public void test(){ PromotionActivity activity ; String promotion = "FANXIAN"; switch (promotion){ case "FANXIAN" : activity = new PromotionActivity(new FanXianPromotionStrategy()); break; case "LIJIAN": activity = new PromotionActivity(new LiJianPromotionStrategy()); break; case "MANJIAN": activity = new PromotionActivity(new ManJianPromotionStrategy()); break; default: activity = null; } if(!ObjectUtils.isEmpty(activity)){ activity.execute(); } } }
|
小结
1 2
| 这种方式和应用交互容易使代码过于臃肿,因为每次需要的时候都会创建策略对象和包装策略的对象,并且过多的选择判断, 整个代码看起来不优雅。
|
优化后版本
策略工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package com.design.pattern.strategy.v2;
import java.util.Map; import java.util.concurrent.ConcurrentHashMap;
public class PromotionStrategyFactory {
private final static Map<String,PromotionStrategy> PROMOTION_STRATEGY_MAP = new ConcurrentHashMap<>();
static { PROMOTION_STRATEGY_MAP.put(PromotionKey.LIJIAN_STRATEGY,new LiJianPromotionStrategy()); PROMOTION_STRATEGY_MAP.put(PromotionKey.FANXIAN_STRATEGY,new FanXianPromotionStrategy()); PROMOTION_STRATEGY_MAP.put(PromotionKey.MANJIAN_STRATEGY,new ManJianPromotionStrategy()); }
private PromotionStrategyFactory(){}
public static PromotionStrategy getPromotionStrategy(String strategy){ return PROMOTION_STRATEGY_MAP.get(strategy); }
private interface PromotionKey{
String LIJIAN_STRATEGY = "LIJIAN";
String MANJIAN_STRATEGY = "LIJIAN";
String FANXIAN_STRATEGY = "FANXIAN"; }
}
|
应用层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.design.pattern.strategy.v2; import org.junit.Test; import org.springframework.util.ObjectUtils;
public class Client {
@Test public void test(){
String promotion = "FANXIAN"; PromotionActivity activity = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotion)); if(!ObjectUtils.isEmpty(activity)){ activity.execute(); } } }
|
小结
1 2
| 1. 使用策略工厂防止策略对象频繁创建 2. 策略模式常常结合单例、工厂以及享元模式等使用
|
策略模式源码解析
jdk的Comparator
1
| Comparator就是一个比较策略接口,它有很多的实现,也支持自定义策略,这些策略实现就是一个个策略。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
@SuppressWarnings("unchecked") public static <T> void parallelSort(T[] a, Comparator<? super T> cmp) { if (cmp == null) cmp = NaturalOrder.INSTANCE; int n = a.length, p, g; if (n <= MIN_ARRAY_SORT_GRAN || (p = ForkJoinPool.getCommonPoolParallelism()) == 1) TimSort.sort(a, 0, n, cmp, null, 0, 0); else new ArraysParallelSortHelpers.FJObject.Sorter<T> (null, a, (T[])Array.newInstance(a.getClass().getComponentType(), n), 0, n, 0, ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ? MIN_ARRAY_SORT_GRAN : g, cmp).invoke(); }
|
Spring的Resource
1
| Resource是资源访问接口,它就是一个资源访问相关的策略接口,它有很多的实现类,也就意味着有很多访问资源的策略。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package org.springframework.core.io;
import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import org.springframework.lang.Nullable;
public interface Resource extends InputStreamSource { boolean exists();
default boolean isReadable() { return this.exists(); }
default boolean isOpen() { return false; }
default boolean isFile() { return false; }
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(this.getInputStream()); }
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String var1) throws IOException;
@Nullable String getFilename();
String getDescription(); }
|
Spring的InstantiationStrategy
1
| Spring初始化策略接口,它的实现类:SimpleInstantiationStrategy和CglibSubclassingInstantiationStrategy初始化策略
|