一、介绍
1. 策略模式可以理解为if-else的优化。
2. 几个概念:策略接口、策略实现类、上下文
多个类的业务流程相似,区别仅仅在于某个或者某几个行为不同,那么我们可以把其他相同的业务流程抽出来做上下文类,对于某个或某几个不同的行为的实现我们建不同的策略实现类,这些策略实现类实现了策略接口中的方法。
业务流程里的一个行为,抽象为策略接口中的一个方法;这个行为有几种不同的算法,就有几种不同的策略实现类。
3. 结合下图说明:
(1) 上下文类Context:
上下文类使用策略接口。
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}
(2) 策略接口Strategy:
策略接口定义行为。
public interface Strategy {
public int doOperation(int num1, int num2);
}
(3) 策略实现类OperationAdd、OperationSubtract、OperationMultiply
策略实现类实现某个行为的不同算法。
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class OperationSubtract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
(4) 测试一下
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationSubtract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
4. 策略模式的使用不必硬搬以上demo,在参考的几篇文章里可以了解到更多的用法。
只要有策略接口和策略实现类即可,根据传入参数不同,利用Java的多态性创造不同的实现类。
二、策略模式在JDK中的使用
1. 线程池ThreadPoolExecutor
其中的参数,拒绝策略就是个策略接口。
策略接口: RejectedExecutionHandler
策略实现类:
一般我们用的拒绝策略是在concurrent包下的,ThreadPoolExecutor的几个内部类。AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy
2. 比较器Comparator
他就是一个策略接口,有许多行为(方法)。
他没有策略实现类,一般都是在使用的时候,我们自己创建临时的策略实现类,作为参数传递。
如:
Arrays.sort(T[],Comparator<? super T> c);
Collections.sort(List<T> list,Comparator<? super T> c);
Comparator的简单使用如下:
/**
* @author 嘉心糖的嘉
*/
public class Student {
private Integer id;
private String name;
Student(Integer id, String name){
this.id = id;
this.name = name;
}
Student(){}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [id=" + this.getId() + ", name=" + this.getName() + "]";
}
}
/**
* @author 嘉心糖的嘉
*/
public class ComparatorTest {
public static void main(String[] args) {
List<Student> studentList = new ArrayList<>();
studentList.add(new Student(1,"student1"));
studentList.add(new Student(2,"student2"));
studentList.add(new Student(3,"student3"));
studentList.add(new Student(4,"student4"));
// Collections.sort(studentList, new Comparator<Student>() {
// @Override
// public int compare(Student o1, Student o2) {
// return o2.getId() - o1.getId();
// }
// });
//使用Java8新特性,lambda表达式
Collections.sort(studentList, (o1, o2) -> o2.getId() - o1.getId());
System.out.println("排序好的list如下:");
System.out.println(studentList);
}
}
三、例子
以下一个Springboot里的策略模式的小例子,涉及到简单工厂模式、bean的生命周期、枚举类的使用,可以巩固其他知识点。这里的例子是我自己在springboot项目中使用elasticsearch中的总结,其他例子可以在参考中的几篇文章里去阅读。
1. 有这样的需求:
数据库中有一张数据总表,每条数据都有不同的查看权限。核心平台1能够检索到所有数据;调用核心平台1接口的平台2、3分别只能检索对应权限的数据。
2. 分析:
检索数据只需要一个接口,只需对三个平台的身份加以区分,构造不同的查询条件即可解决。
3. 代码与注解:
(1)枚举类PlatForm
/**
* @author:嘉心糖的嘉
* 枚举三种平台
*/
public enum Platform {
P_PLATFORM1("0","platform1"),
P_PLATFORM2("1","platform2"),
P_PLATFORM3("2","platform3");
private String type;
private String name;
Platform(String type, String name) {
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
Platform() {
}
}
(2)策略接口 QueryBuilderService
行为是生成queryBuilder
/**
* @author: 嘉心糖的嘉
*/
public interface QueryBuilderService {
QueryBuilder queryBuilderGenerate();
}
(3)三种策略实现类PF1QueryBuilderService、PF2QueryBuilderService、PF3QueryBuilderService
实现InitializingBean接口,重写afterPropertiesSet方法,在bean创建后,把这个类注册到工厂中。
关于bean的生命周期,请看参考中的文章,十分详细。
/**
* @author: 嘉心糖的嘉
*/
@Service
public class PF1QueryBuilderService implements QueryBuilderService, InitializingBean {
@Override
public QueryBuilder queryBuilderGenerate() {
//平台1能看到所有数据
MatchAllQueryBuilder builder = QueryBuilders.matchAllQuery();
return builder;
}
@Override
public void afterPropertiesSet() throws Exception {
QueryBuilderServiceFactory.register(Platform.P_PLATFORM1, this);
}
}
/**
* @author: 嘉心糖的嘉
*/
@Service
public class PF2QueryBuilderService implements QueryBuilderService, InitializingBean {
@Override
public QueryBuilder queryBuilderGenerate() {
//只能看到开放平台2权限的数据
MatchPhraseQueryBuilder matchPhraseQueryBuilder2 = QueryBuilders.matchPhraseQuery("platform2", 1);
QueryBuilder builder = QueryBuilders.boolQuery().must(matchPhraseQueryBuilder2);
return builder;
}
@Override
public void afterPropertiesSet() throws Exception {
QueryBuilderServiceFactory.register(Platform.P_PLATFORM2, this);
}
}
/**
* @author: 嘉心糖的嘉
*/
@Service
public class PF3QueryBuilderService implements QueryBuilderService, InitializingBean {
@Override
public QueryBuilder queryBuilderGenerate() {
//只能看到开放平台3权限的数据
MatchPhraseQueryBuilder matchPhraseQueryBuilder1 = QueryBuilders.matchPhraseQuery("platform3", 1);
QueryBuilder builder = QueryBuilders.boolQuery().must(matchPhraseQueryBuilder1);
return builder;
}
@Override
public void afterPropertiesSet() throws Exception {
QueryBuilderServiceFactory.register(Platform.P_PLATFORM3, this);
}
}
(4)工厂 QueryBuilderServiceFactory
/**
* @author: 嘉心糖的嘉
*/
public class QueryBuilderServiceFactory {
private static Map<String, QueryBuilderService> queryBuilderServiceMap = new ConcurrentHashMap<>();
public static QueryBuilderService getByPlatformType(String type){
return queryBuilderServiceMap.get(type);
}
public static void register(Platform platform, QueryBuilderService queryBuilderService){
queryBuilderServiceMap.put(platform.getType(), queryBuilderService);
}
}
(5)上下文调用
QueryBuilder builder = QueryBuilderServiceFactory.getByPlatformType(statue).queryBuilderGenerate();
然后根据业务需求,对检索条件构造器builder进行添加即可。
总结:
- 策略模式的核心就是策略接口和多个策略实现类。策略接口定义几个行为,这几个行为在策略实现类里分别有不同的实现方式。利用Java的多态性,去完成这种行为。
- 与if-else相比,策略模式的扩展性更好,更容易维护。但是也不能盲目追求策略模式,一些不是很复杂的逻辑if-else简单的话还是可以用if-else。
- 策略如果过多的话会导致策略类的膨胀。
参考:
别在再满屏的 if、else 了,试试策略模式,真香!!_Java技术栈,分享最主流的Java技术-CSDN博客
Java设计模式之行为型:策略模式_张维鹏的博客-CSDN博客
字节二面:如何用策略模式,优化你代码里的的if-else?_敖丙-CSDN博客