策略模式的实际应用
平时学习设计模式应用的地方有限,只有阅读一些框架源码的时候可以比较频繁的见到。最近正好有一个业务场景符合策略模式。
业务场景
搜索某一种物品以及根据规则搜索其相似物品,一共有三种搜索规则 SA,SB,SC。
通常可以使用 switch 或者 if else 结合枚举实现,如果后期增加新的搜索策略,有可能会出现遗漏的情况,可维护性差,代码冗余可读性差。所以我决定采用策略模式实现。
实现
我们采用 guava 提供的 immutableMap 存储策略,key 为对应的策略枚举,value 为对应的方法。
这里可以思考一下如何将方法存入 value。
Guava ImmutableMap
根据guava文档所描述的,ImmutableMap是一个不可变的Map对象,其构造器如下所示,用于创建不可变Map实例。Guava提供了每个java.util的不可变版本。使用 ImmutableMap 映射 。每当我们尝试修改它时,它都会抛出 UnsupportedOperationException。
为什么需要不可变的集合呢?
- 保证线程安全:在并发程序中,使用Immutable既保证线程安全性,也大大增强了并发时的效率(跟并发锁方式相比)。尤其当一个对象是值对象时,更应该考虑采用Immutable方式;
- 被不可信的类库使用时会很安全;
- 如果一个对象不需要支持修改操作(mutation),将会节省空间和时间的开销;经过分析,所有不可变的集合实现都比可变集合更加有效地利用内存;
- 可以当作一个常量来对待,并且这个对象在以后也不会被改变。 将一个对象复制一份成immutable的,是一个防御性编程技术。
ImmutableMap 完全符合我们策略模式存储搜索模式的需求
Enum
枚举类型定义策略类型,后期有新加的类型便于维护,遵循 OOP 开闭原则。
public enum SearchType {
SA('1', 'sa'),
SB('2', 'sb'),
SC('3', 'sc');
private static final Map<String, String> codeTypeMap =
Stream.of(values()).collect(
Collectors.toMap(CompoundSearchType::getCode, CompoundSearchType::getType)
);
private String code;
private String type;
SearchType(String code, String type) {
this.code = code;
this.type = type;
}
public static String getTypeByCode(String code) {
return codeTypeMap.get(code);
}
// Getter and Setter
}
Function
采用函数式编程存储方法至 Map(本质是实现匿名内部类),使用方法的时候再传入参数
public class Strategy {
/**
* 有参有返回值接口
*/
@FunctionalInterface
public interface Function<T, R, E extends Throwable> {
R apply(T t) throws E;
}
}
Initialize
业务代码中使用
@Service
public class Service implements IService {
// initialize strategy map
private final Map<String, Strategy.Function> searchStrategyMap = ImmutableMap.<String, Strategy.Function>builder()
.put(SearchType.SA.getType(), (Strategy.Function<SearchFilter, List<String>, Exception>) filter -> searchA(filter))
.put(SearchType.SB.getType(), (Strategy.Function<SearchFilter, List<String>, Exception>) filter -> searchB(filter))
.put(SearchType.SC.getType(), (Strategy.Function<SearchFilter, List<String>, Exception>) filter -> searchC(filter))
.build();
@Override
public List<String> strategySearch(SearchType searchType, String filter) {
Strategy.Function function = searchStrategyMap.get(searchType.getType());
if (ObjectUtils.isEmpty(function)) throw new RuntimeExcepiton("查询类型不存在");
return (List<String>) function.apply(filter);
}
private List<String> searchA(String filter) {
// Strategy A
return list;
}
private List<String> searchB(String filter) {
// Strategy B
return list;
}
private List<String> searchC(String filter) {
// Strategy C
return list;
}
}
小结
这样代码是不是清爽了很多,网上许多结合 Spring 的策略模式都是利用注解加多态实现的,针对稍微有些规模的设计可以使用,比如文件存储策略,或者全文检索策略可以使用。但是针对这种中小型业务代码没有必要大动干戈,可以采用 map 结合 lambda 表达式小规模的使用,后期如果进行代码优化也比较方便。设计模式的应用不仅可以大到系统架构级别的设计,也可以应用到平时一些小的业务场景,最重要的是合适的场景选择合适的方法。