策略模式的介绍和简单使用

一、介绍

1. 策略模式可以理解为if-else的优化。

2. 几个概念:策略接口策略实现类上下文

多个类的业务流程相似,区别仅仅在于某个或者某几个行为不同,那么我们可以把其他相同的业务流程抽出来做上下文类,对于某个或某几个不同的行为的实现我们建不同的策略实现类,这些策略实现类实现了策略接口中的方法。

业务流程里的一个行为,抽象为策略接口中的一个方法;这个行为有几种不同的算法,就有几种不同的策略实现类。

3. 结合下图说明:

策略模式的 UML 图

(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进行添加即可。


总结:

  1. 策略模式的核心就是策略接口和多个策略实现类。策略接口定义几个行为,这几个行为在策略实现类里分别有不同的实现方式。利用Java的多态性,去完成这种行为。
  2. 与if-else相比,策略模式的扩展性更好,更容易维护。但是也不能盲目追求策略模式,一些不是很复杂的逻辑if-else简单的话还是可以用if-else。
  3. 策略如果过多的话会导致策略类的膨胀。


参考:

策略模式 | 菜鸟教程

别在再满屏的 if、else 了,试试策略模式,真香!!_Java技术栈,分享最主流的Java技术-CSDN博客

Java设计模式之行为型:策略模式_张维鹏的博客-CSDN博客

字节二面:如何用策略模式,优化你代码里的的if-else?_敖丙-CSDN博客

【java】Comparator的用法_峰峯的专栏-CSDN博客_comparator

谈谈你对Spring Bean生命周期的理解【面试】_夏夜凉月-CSDN博客_springbean的生命周期面试

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值