目录

一、定义

二、策略模式案例分析

三、策略模式应用分析

四、总结


一、定义

策略模式是一种比较简单的模式,其定义如下:

定义一系列的算法,将他们一个一个封装起来,然后在运行期间可以动态改变类执行的算法(动态改变类的行为)。策略模式属于行为型模式。(封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法)。在日常生活中有挺多策略模式的例子,如旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略;又比如我们设计一个排序算法,可能有很多种实现方法,如选择排序、冒泡排序等,每一种排序方式都是一种策略等等。

策略模式的通用类图如下:

策略模式使用的就是面向对象的继承和多态机制,非常容易理解和掌握,我们来看看策略模式的三个角色:

  • 环境类(Context):上下文角色,用来操作策略的上下文环境,负责和具体的策略实现交互;
  • 抽象策略类(Strategy): 抽象策略角色,通常为接口,定义每个策略或算法必须具有的方法和属性;
  • 具体策略类(ConcreteStrategy): 具体策略角色,每一种策略算法的具体实现;

二、策略模式案例分析

假设刚好有一个需求,我们要对两个数进行数学运算,我们都知道数学运算有加法、减法、乘法、除法,其实每一种运算方式就是一个具体的策略,如果不使用策略模式的话,我们可以通过if-else判断来实现,但是这不是很好的设计,扩展性不好,例如后面需要增加一种运算方式的时候,就需要再增加if-else判断。

为了使我们的程序扩展性更高,我们采用策略模式来实现这一需求。总体的类图如下:

我们先定义一个运算类型的枚举:

/*** 计算类型的枚举*/
public enum CalType {ADD("add", "相加"),SUBSTRACT("substract", "相减"),MULTIPLY("multiply", "相乘"),DIVIDE("divide", "相除"),;private String value;private String description;CalType(String value, String description) {this.value = value;this.description = description;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public static CalType getStrategyType(String operateType) {if (Objects.equals(CalType.ADD.getValue(), operateType)) {return CalType.ADD;} else if (Objects.equals(CalType.SUBSTRACT.getValue(), operateType)) {return CalType.SUBSTRACT;} else if (Objects.equals(CalType.MULTIPLY.getValue(), operateType)) {return CalType.MULTIPLY;} else if (Objects.equals(CalType.DIVIDE.getValue(), operateType)) {return CalType.DIVIDE;}return CalType.ADD;}}

对照前面我们分析的各种角色,我们需要定义一个抽象策略接口,它定义了两个数的运算方法:

/*** 抽象策略接口*/
public interface AbstractCalculationStrategy {/*** 计算方法** @param a* @param b* @return*/Integer calculate(Integer a, Integer b);/*** 记录操作类型** @return*/CalType calType();
}

接下来定义四种运算方式:加法具体策略、减法具体策略、乘法具体策略、除法具体策略,代码如下:

/*** 具体策略接口: 加法运算*/
public class AddCalculateStrategy implements AbstractCalculationStrategy {@Overridepublic Integer calculate(Integer a, Integer b) {return a + b;}@Overridepublic CalType calType() {return CalType.ADD;}
}/*** 具体策略接口:减法运算*/
public class SubstractCalculateStrategy implements AbstractCalculationStrategy {@Overridepublic Integer calculate(Integer a, Integer b) {return a - b;}@Overridepublic CalType calType() {return CalType.SUBSTRACT;}
}/*** 具体策略接口: 乘法运算*/
public class MultiplyCalculateStrategy implements AbstractCalculationStrategy {@Overridepublic Integer calculate(Integer a, Integer b) {return a * b;}@Overridepublic CalType calType() {return CalType.MULTIPLY;}
}/*** 具体策略接口: 除法运算*/
public class DivideCalculateStrategy implements AbstractCalculationStrategy {@Overridepublic Integer calculate(Integer a, Integer b) {return a / b;}@Overridepublic CalType calType() {return CalType.DIVIDE;}
}

接下来我们需要一个Context上下文类来封装具体策略的调用,这里可以与其他设计模式混合使用,比如这里结合了简单工厂模式,解决了原始策略模式需要暴露具体策略接口的问题。

Context上下文角色的代码如下:

/*** 策略上下文: 封装所有的策略类* 这里结合了简单工厂模式,解决了原始策略模式需要暴露具体策略接口的问题*/
public class CalculationStrategyFactory {/*** 存储所有具体策略接口的容器* 如集成了Spring框架,可以使用自动注入或者@PostConstruct进行注入,避免新增一个具体策略接口,策略上下文类也需要更改;* 当然也可以使用Map来存储,本例中使用List存储;*/private static List<AbstractCalculationStrategy> strategies = new ArrayList<>();static {strategies.add(new AddCalculateStrategy());strategies.add(new SubstractCalculateStrategy());strategies.add(new MultiplyCalculateStrategy());strategies.add(new DivideCalculateStrategy());}/*** 根据操作类型返回一个具体策略接口** @param operateType* @return*/public static AbstractCalculationStrategy selectStrategy(String operateType) {CalType calType = CalType.getStrategyType(operateType);AbstractCalculationStrategy calculationStrategy = strategies.stream().filter(e -> Objects.equals(e.calType().getValue(), calType.getValue())).findFirst().orElse(null);if (Objects.isNull(calculationStrategy)) {throw new UnsupportedOperationException();}return calculationStrategy;}}

最后,我们写一个场景类测试一下:

/*** 场景类*/
public class Client {public static void main(String[] args) {//获取对应的具体策略接口AbstractCalculationStrategy addStrategy = CalculationStrategyFactory.selectStrategy(CalType.ADD.getValue());System.out.println("加法->计算结果:" + addStrategy.calculate(10, 5));AbstractCalculationStrategy subStrategy = CalculationStrategyFactory.selectStrategy(CalType.SUBSTRACT.getValue());System.out.println("减法->计算结果:" + subStrategy.calculate(10, 5));AbstractCalculationStrategy mulStrategy = CalculationStrategyFactory.selectStrategy(CalType.MULTIPLY.getValue());System.out.println("乘法->计算结果:" + mulStrategy.calculate(10, 5));AbstractCalculationStrategy divStrategy = CalculationStrategyFactory.selectStrategy(CalType.DIVIDE.getValue());System.out.println("除法->计算结果:" + divStrategy.calculate(10, 5));}
}

运行结果:

加法->计算结果:15
减法->计算结果:5
乘法->计算结果:50
除法->计算结果:2

可见,根据传入的不同策略算法返回对应的具体策略,然后返回计算之后的结果,这样就避免了过多的if-else判断切换不同的策略算法。

策略模式的重点就是封装角色,它是借用了代理模式的思路,策略模式和代码模式的区别就是策略模式的封装角色和被封装的策略类不用是同一个接口,如果是同一个接口就成了代理模式。

三、策略模式应用分析

在JDK中使用集合排序时用到的Comparator接口就使用到了策略模式,如下:

public class SourceCode {public static void main(String[] args) {List<Student> studentList = new ArrayList<>();studentList.add(new Student(UUID.randomUUID().toString(), "张三", 22));studentList.add(new Student(UUID.randomUUID().toString(), "李四", 25));studentList.add(new Student(UUID.randomUUID().toString(), "王五", 18));//抽象策略类 public interface Comparator<T> { }//具体策略角色Comparator<Student> comparator = (student1, student2) -> student1.getAge() - student2.getAge();//Context上下文角色,传入具体的策略排序算法//升序Collections.sort(studentList, comparator);System.out.println(studentList);comparator = (student1, student2) -> student2.getAge() - student1.getAge();//降序Collections.sort(studentList, comparator);System.out.println(studentList);}
}class Student {private String id;private String name;private Integer age;public Student(String id, String name, Integer age) {this.id = id;this.name = name;this.age = age;}public Integer getAge() {return age;}@Overridepublic String toString() {return "Student{" +"id='" + id + '\'' +", name='" + name + '\'' +", age=" + age +'}';}
}

如上代码分析各个角色如下:

  • Comparator:抽象策略角色,是一个接口,定义了比较对象的抽象方法compare();
int compare(T o1, T o2);
  • (student1, student2) -> student1.getAge() - student2.getAge():具体策略算法角色,比如上面指定的升序、降序两种排序策略;
  • Collections.sort(studentList, comparator):上下文角色,传入不同的策略算法,获得不同的行为,通过源码可见Colleaction.sort()底层调用了Arrays.sort(),根据不同的策略进行排序。
public static <T> void sort(T[] a, Comparator<? super T> c) {if (c == null) {sort(a);} else {if (LegacyMergeSort.userRequested)legacyMergeSort(a, c);elseTimSort.sort(a, 0, a.length, c, null, 0, 0);}
}

四、总结

策略模式的优点:

  • 避免过多的if-else判断切换不同的算法;
  • 扩展性比较好,增加新的策略类,不需要修改原有代码,符合开闭原则;
  • 动态切换算法策略,动态改变类的行为;

策略模式的缺点:

  • 如果策略算法很多,可能会产生很多具体策略类,维护起来就比较困难;
  • 必须对外暴露所有的具体策略类:上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则是相违背的。幸运的是,我们可以使用其他模式来修正这个缺陷,比如工厂方法模式、代理模式等。

策略模式的使用场景:

  • 系统需要动态地切换不同的算法;
  • 多个类只有在算法或行为上稍有不同的场景;
  • 需要屏蔽算法规则的场景;

策略模式的注意事项:

如果系统中的一个策略家族的具体策略数量超过4个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则日后系统就不太好维护。

设计模式 (二十一) 策略模式相关推荐

  1. java计数器策略模式_java设计模式(二十一)--策略模式

    对于策略模式,我在很多面试题上看到过考察这一类的问题,这种模式也的确比较好用. 我感觉这种模式就是将不同实现的方法放到一个接口中,然后通过实现这个接口来实现不同的运行结果,这种模式有三部分构成: 策略 ...

  2. Java设计模式之十一 ---- 策略模式和模板方法模式

    前言 在上一篇中我们学习了行为型模式的访问者模式(Visitor Pattern)和中介者模式(Mediator Pattern).本篇则来学习下行为型模式的两个模式,策略模式(Strategy Pa ...

  3. Java进阶篇设计模式之十一 ---- 策略模式和模板方法模式

    前言 在上一篇中我们学习了行为型模式的访问者模式(Visitor Pattern)和中介者模式(Mediator Pattern).本篇则来学习下行为型模式的两个模式,策略模式(Strategy Pa ...

  4. Java设计模式(十二) 策略模式

    策略模式介绍 策略模式定义 策略模式(Strategy Pattern),将各种算法封装到具体的类中,作为一个抽象策略类的子类,使得它们可以互换.客户端可以自行决定使用哪种算法. 策略模式类图 策略模 ...

  5. 设计模式(二)--策略模式

    1.策略模式 在策略模式中,我们可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里,每一个封装算法的类我们都可以称之为一种策略(Strategy),为了保证这些策略在使用时具有一 ...

  6. 【白话设计模式二】外观模式(Facade)

    为什么80%的码农都做不了架构师?>>>    #0 系列目录# 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factory) [白话设计模式二] ...

  7. 设计模式のStrategyPattern(策略模式)----行为模式

    一.问题产生背景 当我们进行一系列处理时(员工工资核算,会员管理,计算器,优惠活动),会有很多相似的算法和处理过程,只是由于具体的算法的差异,导致必须不同处理.这些处理和客户端无关,我们可以把这些算法 ...

  8. Java描述设计模式(22):策略模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 每年双十一,各大电商平台会推出不同的满减策略,当用户的消费金额满一定额度后,会进行减去一定的优惠额度,从而来一波清仓甩卖,使用策 ...

  9. 从王者荣耀看设计模式(一.策略模式)

    从王者荣耀看设计模式(策略模式) 一:简介 游戏开始前,玩家需要选择英雄,再根据所选择的阵容自由选择召唤师技能,游戏开始,玩家可以控制英雄进行普通攻击和使用召唤师技能攻击 二:策略模式 策略模式将可变 ...

  10. 【设计模式-适配器,策略模式,和责任链】

    文章目录 一.适配器模式(Adaptor Pattern) 1. 定义 2. 生活例子(百度) 3. 适配器角色 4. 适配器模式分类 5. 实例 二.策略模式(Strategy Pattern) 1 ...

最新文章

  1. (原创)Python文件与文件系统系列(5)——stat模块
  2. java安装 环境配置
  3. matlab画图入门篇--各种基本图形绘制的函数与实例【转载】
  4. 嵌入式linux gif 缩放_嵌入式环境动力监控主机
  5. PTA--一元多项式的乘法与加法运算
  6. python3.8使用pyttsx3报错_使用pyttsx3实现python语音播报
  7. 女程序员上班第一件事:调整IDE颜色以适配今天的衣着妆容
  8. Android 代码重构案例
  9. 百度云高速下载的两种方法
  10. JavaScript速成
  11. eNSP路由器启动不了
  12. 各类dp的总结+例题
  13. LaTex - 插入公式 (从MathType公式编辑器导入到LaTex中)
  14. 对训练样本分布不均的思考
  15. zabbix 5.0所有依赖包_最详细的 Zabbix 使用教程
  16. Spring包含JAR的详解
  17. 学习Python可以做什么工作?选哪些工作方向?
  18. 基于OCILIB的oracle数据库操作总结及自动生成Model和Dao的工具
  19. Mysql 的自增主键达到最大值,怎么办
  20. 智能人员考勤系统(C语言)

热门文章

  1. mysql互为主从有什么问题吗_mysql 互为主从复制常见问题
  2. DeepRacer 根据路线计算Action Space RaceLine_Speed_ActionSpace
  3. 算法突击训练营:开学第1课 听课总结
  4. 易筋SpringBoot 2.1 | 第廿三篇:SpringBoot之Docker入门
  5. 算法:Regular Expression Matching(正则表达式匹配)
  6. jxl java mer_导出报表出错,有没有大神懂得
  7. java 注解解析_Java知识点总结(注解-解析注解)
  8. 神经图灵机NTM —— 元学习
  9. discuz mysql语句_discuz 数据库插入
  10. flutter 点击旋转动画_flutter RotationTransition实现旋转动画