简介
策略模式是指有一定行动内容的相对稳定的策略名称,策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法
策略模式:
- 定义了一组算法(业务规则)
- 封装了每个算法
- 这族的算法可互换代替
组成
- 抽象策略角色: 策略类,通常由一个接口或者抽象类实现
- 具体策略角色:包装了相关的算法和行为
- 环境角色:持有一个策略类的引用,最终给客户端调用
应用场景
- 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为
- 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现
- 对客户隐藏具体策略(算法)的实现细节,彼此完全独立
- 一个类定义了多种行为,并且这些行为在类的操作中以多个条件语句的形式出现
优点:
- 策略模式符合开闭原则
- 避免使用多重条件转移语句,如if...else...语句、switch 语句
- 使用策略模式可以提高算法的保密性和安全性
缺点:
- 客户端必须知道所有的策略,并且自行决定使用哪一个策略类
- 代码中会产生非常多策略类,增加维护难度
实际应用
抽象策略类
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
public interface ChargeStrategy {
/**
* 计算收费
*
* @param cost
* @return
*/
double charge(long cost);
}
具体策略类-内部收费类
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
@TaxTypeAnnotation(taxType = ChargeType.INTERNAL)
class InternalStrategy implements ChargeStrategy {
@Override
public double charge(long cost) {
final double taxRate = 0.2;
return cost * taxRate;
}
}
具体策略类-外部收费类
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
@TaxTypeAnnotation(taxType = ChargeType.EXTERNAL)
class ExternalTaxStrategy implements ChargeStrategy {
@Override
public double charge(long cost) {
return cost;
}
}
收费类型定义
public enum ChargeType {
INTERNAL, EXTERNAL
}
策略工厂
通过if-else获取策略类
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
@Component
public class ChargeStrategyFactory {
public static ChargeStrategy getChargeStrategy(ChargeType taxType) throws Exception {
if (taxType == ChargeType.INTERNAL) {
return new InternalStrategy();
} else if (taxType == ChargeType.EXTERNAL) {
return new ExternalTaxStrategy();
} else {
throw new Exception("未配置相应策略");
}
}
}
@Test
public void test1(){
try {
ChargeStrategy chargeStrategy = ChargeStrategyFactory.getChargeStrategy(ChargeType.INTERNAL);
double charge = chargeStrategy.charge(100);
System.out.println("内部价格:"+charge);
chargeStrategy = ChargeStrategyFactory.getChargeStrategy(ChargeType.EXTERNAL);
charge = chargeStrategy.charge(100);
System.out.println("外部价格:"+charge);
} catch (Exception e) {
e.printStackTrace();
}
}
输出结果:
内部价格:20.0
外部价格:100.0
- 如图,通过传入不同的收费类型,由策略工厂进行判断,赋予相应的处理策略
- 可以看到,如果通过if语句来获取不同的税策略,当增加新的税策略时就不得不修改已有代码,当方法很多时,就不那么好看,同时也增加了复杂度
通过Map获取策略类
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
public class MapChargeStrategyFactory {
/**
*存储策略
*/
static Map<ChargeType, ChargeStrategy> chargeStrategyMap = new HashMap<>();
// 注册默认策略
static {
registerChargeStrategy(ChargeType.INTERNAL, new InternalStrategy());
registerChargeStrategy(ChargeType.EXTERNAL, new ExternalTaxStrategy());
}
/**
* 提供注册策略接口,外部只需要调用此接口接口新增策略
*/
public static void registerChargeStrategy(ChargeType taxType, ChargeStrategy taxStrategy) {
chargeStrategyMap.put(taxType, taxStrategy);
}
/**
* 通过map获取策略,当增加新的策略时无需修改代码
*/
public static ChargeStrategy getChargeStrategy(ChargeType taxType) throws Exception {
// 当增加新的税类型时,需要修改代码,同时增加圈复杂度
if (chargeStrategyMap.containsKey(taxType)) {
return chargeStrategyMap.get(taxType);
} else {
throw new Exception("未配置相应策略");
}
}
}
@Test
public void test2(){
try {
ChargeStrategy chargeStrategy = MapChargeStrategyFactory.getChargeStrategy(ChargeType.INTERNAL);
double charge = chargeStrategy.charge(100);
System.out.println("内部价格:"+charge);
chargeStrategy = MapChargeStrategyFactory.getChargeStrategy(ChargeType.EXTERNAL);
charge = chargeStrategy.charge(100);
System.out.println("外部价格:"+charge);
} catch (Exception e) {
e.printStackTrace();
}
}
输出:
内部价格:20.0
外部价格:100.0
- 可以看到,通过map进行获取策略结果相同,进化后if语句没有了,减少了复杂度,增加新的策略后只需调用策略注册接口就好,不需要修改获取策略的代码
通过策略自动注册获取策略类
策略类添加自动注册策略的方法
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
public interface ChargeStrategy {
/**
* 计算收费
*
* @param cost
* @return
*/
double charge(long cost);
/**
* 自动注册策略方法
*/
void register();
}
具体策略实现类
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
@TaxTypeAnnotation(taxType = ChargeType.EXTERNAL)
class ExternalTaxStrategy implements ChargeStrategy {
@Override
public double charge(long cost) {
return cost;
}
@Override
public void register() {
AutoRegisterChargeStrategyFactory.registerChargeStrategy(ChargeType.EXTERNAL, this);
}
}
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
@TaxTypeAnnotation(taxType = ChargeType.INTERNAL)
class InternalStrategy implements ChargeStrategy {
@Override
public double charge(long cost) {
final double taxRate = 0.2;
return cost * taxRate;
}
@Override
public void register() {
AutoRegisterChargeStrategyFactory.registerChargeStrategy(ChargeType.INTERNAL, this);
}
}
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
public class AutoRegisterChargeStrategyFactory {
/**
* 存储策略
*/
static Map<ChargeType, ChargeStrategy> chargeStrategyMap = new HashMap<>();
static {
autoRegisterTaxStrategy();
}
/**
* 通过map获取策略,当增加新的策略时无需修改代码
*/
public static ChargeStrategy getChargeStrategy(ChargeType taxType) throws Exception {
// 当增加新的税类型时,需要修改代码,同时增加圈复杂度
if (chargeStrategyMap.containsKey(taxType)) {
return chargeStrategyMap.get(taxType);
} else {
throw new Exception("未配置相应策略");
}
}
/**
* 提供税注册策略接口
*/
public static void registerChargeStrategy(ChargeType chargeType, ChargeStrategy chargeStrategy) {
chargeStrategyMap.put(chargeType, chargeStrategy);
}
/**
* 自动注册策略
*/
private static void autoRegisterTaxStrategy() {
try {
// 通过反射找到所有的策略子类进行注册
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage(ChargeStrategy.class.getPackage().getName()))
.setScanners(new SubTypesScanner()));
Set<Class<? extends ChargeStrategy>> taxStrategyClassSet = reflections.getSubTypesOf(ChargeStrategy.class);
if (taxStrategyClassSet != null) {
for (Class<?> clazz : taxStrategyClassSet) {
ChargeStrategy chargeStrategy = (ChargeStrategy) clazz.newInstance();
// 调用策略的自注册方法
chargeStrategy.register();
}
}
} catch (InstantiationException | IllegalAccessException e) {
// 自行定义异常处理
e.printStackTrace();
}
}
}
@Test
public void test3(){
try {
ChargeStrategy chargeStrategy = AutoRegisterChargeStrategyFactory.getChargeStrategy(ChargeType.INTERNAL);
double charge = chargeStrategy.charge(100);
System.out.println("内部价格:"+charge);
chargeStrategy = AutoRegisterChargeStrategyFactory.getChargeStrategy(ChargeType.EXTERNAL);
charge = chargeStrategy.charge(100);
System.out.println("外部价格:"+charge);
} catch (Exception e) {
e.printStackTrace();
}
}
输出
内部价格:20.0
外部价格:100.0
- 可以看到,通过策略自动注册进行获取策略结果相同,当添加新的税策略时,就完全不需要修改已有的策略工厂代码,基本完美做到开闭原则,唯一需要修改的是类型定义
通过注解减少耦合
新增加自定义注解类
/**
* @author feiniu
* @description 描述
* @date 2021/10/26
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ChargeTypeAnnotation {
ChargeType taxType();
}
策略去掉了注册方法,添加ChargeTypeAnnotation注解来识别是哪种税类型
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
public interface ChargeStrategy {
/**
* 计算收费
*
* @param cost
* @return
*/
double charge(long cost);
}
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
@ChargeTypeAnnotation(taxType = ChargeType.INTERNAL)
class InternalStrategy implements ChargeStrategy {
@Override
public double charge(long cost) {
final double taxRate = 0.2;
return cost * taxRate;
}
}
/**
* @author feiniu
* @description 描述
* @date 2021/10/11
*/
@ChargeTypeAnnotation(taxType = ChargeType.EXTERNAL)
class ExternalTaxStrategy implements ChargeStrategy {
@Override
public double charge(long cost) {
return cost;
}
}
注解类策略工厂
/**
* @author feiniu
* @description 描述
* @date 2021/10/26
*/
public class AnnotationChargeStrategyFactory {
/**
* 存储策略
*/
static Map<ChargeType, ChargeStrategy> chargeStrategyMap = new HashMap<>();
static {
registerChargeStrategy();
}
/**
* 通过map获取策略,当增加新的策略时无需修改代码
*/
public static ChargeStrategy getChargeStrategy(ChargeType chargeType) throws Exception {
// 当增加新的类型时,需要修改代码
if (chargeStrategyMap.containsKey(chargeType)) {
return chargeStrategyMap.get(chargeType);
} else {
throw new Exception("未配置相应策略");
}
}
/**
* 自动注册策略
*/
private static void registerChargeStrategy() {
// 通过反射找到所有的策略子类进行注册
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage(ChargeStrategy.class.getPackage().getName()))
.setScanners(new SubTypesScanner()));
Set<Class<? extends ChargeStrategy>> taxStrategyClassSet = reflections.getSubTypesOf(ChargeStrategy.class);
if (taxStrategyClassSet != null) {
for (Class<?> clazz : taxStrategyClassSet) {
// 找到类型注解,自动完成策略注册
if (clazz.isAnnotationPresent(ChargeTypeAnnotation.class)) {
ChargeTypeAnnotation taxTypeAnnotation = clazz.getAnnotation(ChargeTypeAnnotation.class);
ChargeType chargeType = taxTypeAnnotation.taxType();
try {
chargeStrategyMap.put(chargeType, (ChargeStrategy) clazz.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
e.getStackTrace();
}
}
}
}
}
}
@Test
public void test4(){
try {
ChargeStrategy chargeStrategy = AnnotationChargeStrategyFactory.getChargeStrategy(ChargeType.INTERNAL);
double charge = chargeStrategy.charge(100);
System.out.println("内部价格:"+charge);
chargeStrategy = AnnotationChargeStrategyFactory.getChargeStrategy(ChargeType.EXTERNAL);
charge = chargeStrategy.charge(100);
System.out.println("外部价格:"+charge);
} catch (Exception e) {
e.printStackTrace();
}
}
- 可以看到,通过注解方式确定策略类的方式获取策略结果相同,是代码更加简洁。
注意: 测试阶段这么写不会出问题,但是小编在通过优化代码时,调用接口,发现这样会导致注入的对象为null值,从而导致程序报空指针异常,起初没发现,不知道为什么按照正常程序开发,会导致通过autowired方式注入为null,经过查阅资料和断点代码,发现了问题所在
- 图中框住的部分,通过反射代码获取类的时候,我们获得类的方式是 class.newInstance, 这种写法没有与Spring容器关联起来获取bean,虽然也能拿到但是如果类里面有@Autowired这种方式注入的对象就会空了
解决方式
通过spring上下文获得类
@Service
public class ApplicationContextHelper implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
/**
* 获取bean
* @param clazz
* @param <T>
* @return
*/
public static <T> T popBean(Class<T> clazz) {
//先判断是否为空
if (applicationContext == null) {
return null;
}
return applicationContext.getBean(clazz);
}
public static <T> T popBean(String name, Class<T> clazz) {
if (applicationContext == null) {
return null;
}
return applicationContext.getBean(name, clazz);
}
}
/**
* @author feiniu
* @description 描述
* @date 2021/10/26
*/
public class AnnotationChargeStrategyFactory {
/**
* 存储策略
*/
static Map<ChargeType, ChargeStrategy> chargeStrategyMap = new HashMap<>();
static {
registerChargeStrategy();
}
/**
* 通过map获取策略,当增加新的策略时无需修改代码
*/
public static ChargeStrategy getChargeStrategy(ChargeType chargeType) throws Exception {
// 当增加新的类型时,需要修改代码
if (chargeStrategyMap.containsKey(chargeType)) {
return chargeStrategyMap.get(chargeType);
} else {
throw new Exception("未配置相应策略");
}
}
/**
* 自动注册策略
*/
private static void registerChargeStrategy() {
// 通过反射找到所有的策略子类进行注册
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage(ChargeStrategy.class.getPackage().getName()))
.setScanners(new SubTypesScanner()));
Set<Class<? extends ChargeStrategy>> taxStrategyClassSet = reflections.getSubTypesOf(ChargeStrategy.class);
if (taxStrategyClassSet != null) {
for (Class<?> clazz : taxStrategyClassSet) {
// 找到类型注解,自动完成策略注册
if (clazz.isAnnotationPresent(ChargeTypeAnnotation.class)) {
ChargeTypeAnnotation taxTypeAnnotation = clazz.getAnnotation(ChargeTypeAnnotation.class);
ChargeType chargeType = taxTypeAnnotation.taxType();
chargeStrategyMap.put(chargeType, (ChargeStrategy) ApplicationContextHelper.popBean(clazz));
}
}
- 通过这种方式就可以解决上述问题,完成自动注册策略的优化