首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

设计模式 —— 策略模式

  • 25-03-07 20:22
  • 2958
  • 9619
blog.csdn.net

目录

一、策略模式定义

二、深入浅出拨开策略模式

1、普通整型排序

2、新增自定义类(猫类)并排序

3、实体类实现 Comparable接口,避免Sort相关类越来越多

4、实体类实现带泛型 Comparable接口,避免重写compareTo方法时类型强转

5、通过比较器实现:实体类比较大小的不同策略


一、策略模式定义

策略模式定义了一系列的方法,并将每个方法封装起来,使每个方法可以相互替代,使用方法本身和使用方法的客户端分割开来,相互独立

大白话:同一个类的同一个方法存在不同策略,且策略根据客户端来决定选择哪种策略适合。

二、深入浅出拨开策略模式

这么介绍可能很抽象,还是较难理解策略模式。下面我们将从实际代码一层层拨开策略模式的知识。

1、普通整型排序

定义一个对 整型 int 排序的类及排序方法(为了好理解,以选择排序为例);应用 T01_Main中 main 方法对其调用排序。

a) 下面是对 整型 int 排序的类及排序方法

  1. public class T01_Sorter {
  2. public static void sort(int[] arr) {
  3. for (int time = 0; time < arr.length - 1; time++) {
  4. int min = time; // 取当前剩下数组第一个元素下标为最小下标
  5. for (int i = time + 1; i < arr.length; i++) {
  6. // 找到比当前元素值更小的下标,从当前数组序列头开始一直到结尾
  7. if (arr[i] < arr[min]) {
  8. min = i;
  9. }
  10. }
  11. //System.out.println("minPos: " + minPos);
  12. // 找到后,将两个值进行交换
  13. swap(arr, min, time);
  14. //System.out.println("经过第" + i + "次循环之后,数组的内容:");
  15. }
  16. }
  17. private static void swap(int[] arr, int i, int j){
  18. int temp = arr[i];
  19. arr[i] = arr[j];
  20. arr[j] = temp;
  21. }
  22. }

b) 下面是应用 T01_Main中 main 方法对其调用排序

  1. public class T01_Main {
  2. public static void main(String[] args) {
  3. int[] a = {9,2,3,5,7,1,4};
  4. T01_Sorter sorter = new T01_Sorter();
  5. sorter.sort(a);
  6. System.out.println(Arrays.toString(a));
  7. }
  8. }

这似乎世界很友好,也能解决当前问题。现在来了个新需求:新增猫类,并需要对其进行排序。于是我们继续分析


2、新增自定义类(猫类)并排序

接到了新需求:新增猫类,并需要对其进行排序。首先,我们先定义一个 Bean 类 猫类 Cat。

  1. public class T02_Cat {
  2. int weight, height;
  3. public T02_Cat(int weight, int height) {
  4. this.weight = weight;
  5. this.height = height;
  6. }
  7. // 定义猫的比较方法
  8. public int compareTo(T02_Cat c) {
  9. if (this.weight < c.weight) return -1;
  10. else if (this.weight > c.weight) return 1;
  11. else return 0;
  12. }
  13. @Override
  14. public String toString() {
  15. return "T03_Cat{" +
  16. "weight=" + weight +
  17. ", height=" + height +
  18. '}';
  19. }
  20. }

再编写一个对猫进行排序的类,因为猫的排序方式是按照身高或体重来排序的,不能复用之前的整型的排序方式

  1. public class T02_Sorter {
  2. public static void sort(T02_Cat[] arr) {
  3. for (int time = 0; time < arr.length - 1; time++) {
  4. int min = time; // 取当前剩下数组第一个元素下标为最小下标
  5. for (int i = time + 1; i < arr.length; i++) {
  6. // 找到比当前元素值更小的下标,从当前数组序列头开始一直到结尾
  7. min = arr[i].compareTo(arr[min]) == -1 ? i : min;
  8. }
  9. //System.out.println("minPos: " + minPos);
  10. // 找到后,将两个值进行交换
  11. swap(arr, min, time);
  12. //System.out.println("经过第" + i + "次循环之后,数组的内容:");
  13. }
  14. }
  15. private static void swap(T02_Cat[] arr, int i, int j){
  16. T02_Cat temp = arr[i];
  17. arr[i] = arr[j];
  18. arr[j] = temp;
  19. }
  20. }

再创建应用类,对猫进行排序

  1. public class T02_Main {
  2. public static void main(String[] args) {
  3. T02_Cat[] a = {new T02_Cat(3,3), new T02_Cat(5,5), new T02_Cat(1,1)};
  4. T02_Sorter sorter = new T02_Sorter();
  5. sorter.sort(a);
  6. System.out.println(Arrays.toString(a));
  7. }
  8. }

这似乎也能解决需求新增类实体并排序问题,但仔细观察我们发现,如果此时又来一个新的需求:新增狗类并排序。如果按照上述思路继续,我们又得增加狗的Bean类、增加狗的排序类。类越来越多,后期维护起来麻烦。

既然意识到此问题,我们应该怎样解决问题呢?实体Bean类、及对应的排序类是一一对应关系,将比较大小的方式统一写在实体Bean类就可以解决类越来越多的问题,是不错的解决方案。


3、实体类实现 Comparable接口,避免Sort相关类越来越多

为了统一实体类的比较大小的方式,实体类需要实现 Comparable 接口,重写 compareTo方法,这样的好写,实体类的比较方法都是统一的,且大家都需要重写 compareTo 方法。

  1. public interface T03_Comparable {
  2. public int compareTo(Object a);
  3. }

接下来在定义实体Bean 类时,使用 implements 关键字来实现 Comarable接口

  1. public class T03_Cat implements T03_Comparable {
  2. int weight, height;
  3. public T03_Cat(int weight, int height) {
  4. this.weight = weight;
  5. this.height = height;
  6. }
  7. // 定义猫的比较方法
  8. @Override
  9. public int compareTo(Object c) {
  10. T03_Cat cat = (T03_Cat)c;
  11. if (this.weight < cat.weight) return -1;
  12. else if (this.weight > cat.weight) return 1;
  13. else return 0;
  14. }
  15. @Override
  16. public String toString() {
  17. return "T03_Cat{" +
  18. "weight=" + weight +
  19. ", height=" + height +
  20. '}';
  21. }
  22. }

排序类 Sorter 不再是对Bean 类进行排序了,而是对接口进行排序。具体比较大小的规则在对应的实体类内部实现,既后续只需要扩展新增对应的实体类(要求实现 implement Compare接口)即可。

  1. public class T03_Sorter {
  2. public static void sort(T03_Comparable[] arr) {
  3. for (int time = 0; time < arr.length - 1; time++) {
  4. int min = time; // 取当前剩下数组第一个元素下标为最小下标
  5. for (int i = time + 1; i < arr.length; i++) {
  6. // 找到比当前元素值更小的下标,从当前数组序列头开始一直到结尾
  7. min = arr[i].compareTo(arr[min]) == -1 ? i : min;
  8. }
  9. //System.out.println("minPos: " + minPos);
  10. // 找到后,将两个值进行交换
  11. swap(arr, min, time);
  12. //System.out.println("经过第" + i + "次循环之后,数组的内容:");
  13. }
  14. }
  15. private static void swap(T03_Comparable[] arr, int i, int j){
  16. T03_Comparable temp = arr[i];
  17. arr[i] = arr[j];
  18. arr[j] = temp;
  19. }
  20. }

应用类对实体类对象进行排序调用保持不变

  1. public class T03_Main {
  2. public static void main(String[] args) {
  3. T03_Cat[] a = {
  4. new T03_Cat(3,3),
  5. new T03_Cat(5,5),
  6. new T03_Cat(1,1)
  7. };
  8. T03_Sorter sorter = new T03_Sorter();
  9. sorter.sort(a);
  10. System.out.println(Arrays.toString(a));
  11. }
  12. }

至此: 实体类通过实现 Compare接口,重写 compareTo比较大小方法;比较类 Sorter只需要对接口进行排序即可,算是解决Sorter不再随着实现类新增而新增问题。

但是,上述这种方式不太方便,每次在实体类重定 compareTo 比较大小方法,都需要进行类型强转。为此,可以引用泛型来避免类型强转问题。补充说明一下:泛型是JavaSE5 引入的一个新概念。


4、实体类实现带泛型 Comparable接口,避免重写compareTo方法时类型强转

第三个版本使用Object,实现类在重写 compareTo方法需要进行类型强转,非常不便,可以使用泛型解决

补充说明:泛型是指在接口中,不确定实现此接口的类型是什么,先由泛型T 代替,在实现类中需要指定泛型类型

  1. // 第三个版本使用Object,实现类需要进行强转;非常不便,可以使用泛型
  2. public interface T04_Comparable {
  3. public int compareTo(T a);
  4. }

实体类实现 Comparable 接口,同时指定泛型类型

  1. // 实体类实现Comparable 时指定泛型类型
  2. public class T04_Cat implements T04_Comparable {
  3. int weight, height;
  4. public T04_Cat(int weight, int height) {
  5. this.weight = weight;
  6. this.height = height;
  7. }
  8. // 定义猫的比较方法
  9. @Override
  10. public int compareTo(T04_Cat cat) {
  11. if (this.weight < cat.weight) return -1;
  12. else if (this.weight > cat.weight) return 1;
  13. else return 0;
  14. }
  15. @Override
  16. public String toString() {
  17. return "T03_Cat{" +
  18. "weight=" + weight +
  19. ", height=" + height +
  20. '}';
  21. }
  22. }

Sorter 类排序还是一样对 Compareable排序,与第三个版本保持不变

  1. public class T04_Sorter {
  2. public static void sort(T04_Comparable[] arr) {
  3. for (int time = 0; time < arr.length - 1; time++) {
  4. int min = time; // 取当前剩下数组第一个元素下标为最小下标
  5. for (int i = time + 1; i < arr.length; i++) {
  6. // 找到比当前元素值更小的下标,从当前数组序列头开始一直到结尾
  7. min = arr[i].compareTo(arr[min]) == -1 ? i : min;
  8. }
  9. //System.out.println("minPos: " + minPos);
  10. // 找到后,将两个值进行交换
  11. swap(arr, min, time);
  12. //System.out.println("经过第" + i + "次循环之后,数组的内容:");
  13. }
  14. }
  15. private static void swap(T04_Comparable[] arr, int i, int j){
  16. T04_Comparable temp = arr[i];
  17. arr[i] = arr[j];
  18. arr[j] = temp;
  19. }
  20. }

应用类与第三个版本应用类保持不变

  1. public class T04_Main {
  2. public static void main(String[] args) {
  3. T04_Cat[] a = {
  4. new T04_Cat(3,3),
  5. new T04_Cat(5,5),
  6. new T04_Cat(1,1)
  7. };
  8. T04_Sorter sorter = new T04_Sorter();
  9. sorter.sort(a);
  10. System.out.println(Arrays.toString(a));
  11. }
  12. }

这似乎能满足大部分场景,答案是:可以!但到了大型后台业务系统中,就可能水土不服了。为什么这么说呢?

我们还是接着前面猫的比较大小的场景,在业务系统中可能需要按照体重比较大小,也有可能按照身高比较大小,即同一个实体类在不同的业务情况下比较大小的方式不一样。按照上述方式依然无法解决此问题(只能解决一种比较大小的方式),难道就没有办法解决了么?答案是:有的!

终于,引入了正题,前面都是铺垫,是为了更好的理解策略模式。


5、通过比较器实现:实体类比较大小的不同策略

先定认猫类,这里猫类不再实现 Comparable 接口,因此该方式只能实现一种比较大小的策略,如下:

  1. public class T05_Cat {
  2. public int weight, height;
  3. public T05_Cat(int weight, int height) {
  4. this.weight = weight;
  5. this.height = height;
  6. }
  7. @Override
  8. public String toString() {
  9. return "T03_Cat{" +
  10. "weight=" + weight +
  11. ", height=" + height +
  12. '}';
  13. }
  14. }

猫类比较大小的不同策略,都是compare 方法,为此先定义一个接口比较器 T05_Comparator

,后续按体重比较策略类、按身高比较策略类都实现 T05_Comparator 接口即可

  1. @FunctionalInterface // 如果确认只有这一个方法,不编写@FunctionalInterface 也是可以的
  2. public interface T05_Comparator {
  3. int compare(T o1, T o2);
  4. }

再定义身高大小比较器、体重大小比较器 

  1. // 按身高大小的比较器
  2. public class T05_CatHeightComparator implements T05_Comparator {
  3. @Override
  4. public int compare(T05_Cat o1, T05_Cat o2) {
  5. if (o1.height > o2.height) return -1;
  6. else if (o1.height < o2.height) return 1;
  7. else return 0;
  8. }
  9. }
  1. // 按体重大小的比较器
  2. public class T05_CatWeightComparator implements T05_Comparator {
  3. @Override
  4. public int compare(T05_Cat o1, T05_Cat o2) {
  5. if (o1.weight < o2.weight) return -1;
  6. else if (o1.weight > o2.weight) return 1;
  7. else return 0;
  8. }
  9. }

再定义排序类,将比较器作为参数传入,按照比较器策略来进行大小比较

  1. // 排序类中,将比较器作为参数传入,按照比较器策略来进行大小比较
  2. public class T05_Sorter {
  3. public void sort(T[] arr, T05_Comparator comparator) {
  4. for (int time = 0; time < arr.length - 1; time++) {
  5. int min = time; // 取当前剩下数组第一个元素下标为最小下标
  6. for (int i = time + 1; i < arr.length; i++) {
  7. // 找到比当前元素值更小的下标,从当前数组序列头开始一直到结尾
  8. min = comparator.compare(arr[i], arr[min]) == -1 ? i : min;
  9. }
  10. //System.out.println("minPos: " + minPos);
  11. // 找到后,将两个值进行交换
  12. swap(arr, min, time);
  13. //System.out.println("经过第" + i + "次循环之后,数组的内容:");
  14. }
  15. }
  16. private void swap(T[] arr, int i, int j){
  17. T temp = arr[i];
  18. arr[i] = arr[j];
  19. arr[j] = temp;
  20. }
  21. }

最后,通过应用类来选择不同的比较策略,按需所取

  1. public class T05_Main {
  2. public static void main(String[] args) {
  3. T05_Cat[] a = {
  4. new T05_Cat(3,3),
  5. new T05_Cat(5,5),
  6. new T05_Cat(1,1)
  7. };
  8. T05_Sorter sorter = new T05_Sorter();
  9. //sorter.sort(a, new T05_CatWeightComparator());
  10. // 采用策略模型:
  11. //sorter.sort(a, new T05_CatHeightComparator());
  12. // 写法上,可以采用lambda 表达式进行
  13. sorter.sort(a, (o1, o2) -> {
  14. T05_Cat cat1 = (T05_Cat)o1;
  15. T05_Cat cat2 = (T05_Cat)o2;
  16. if (cat1.weight < cat2.weight) return -1;
  17. else if (cat1.weight > cat2.weight) return 1;
  18. else return 0;
  19. });
  20. // 如果一个策略每次调用,都需要 new,因此应该把 DefaultStrategy -> Singleton
  21. // 通过配置文件加载策略类名,可以动态调整:对修改关闭、对扩展开放(Extensibility Scalability)
  22. String goodStrategyClassName = "";//PrppertyMgr.get("goodWay");
  23. try {
  24. //T05_CatHeightComparator goodStrategy = (T05_CatHeightComparator)Class.forName(goodStrategyClassName).newInstance();
  25. T05_CatHeightComparator goodStrategy = (T05_CatHeightComparator)Class.forName(goodStrategyClassName).getDeclaredConstructor().newInstance();
  26. sorter.sort(a, goodStrategy);
  27. } catch (InstantiationException e) {
  28. e.printStackTrace();
  29. } catch (IllegalAccessException e) {
  30. e.printStackTrace();
  31. } catch (ClassNotFoundException e) {
  32. e.printStackTrace();
  33. } catch (NoSuchMethodException e) {
  34. e.printStackTrace();
  35. } catch (InvocationTargetException e) {
  36. e.printStackTrace();
  37. }
  38. // 还可以通过加载类名
  39. System.out.println(Arrays.toString(a));
  40. }
  41. }

小总结:

核心点,将策略封装成一个接口,然后可以通过实现策略接口方式实现各式各样的不同策略;应用类调用时按需传入不同的策略即可,或者使用匿名内部类的方式实现接口方法

 


文章最后,给大家推荐一些受欢迎的技术博客链接:

  1. JAVA相关的深度技术博客链接
  2. Flink 相关技术博客链接
  3. Spark 核心技术链接
  4. 设计模式 —— 深度技术博客链接
  5. 机器学习 —— 深度技术博客链接
  6. Hadoop相关技术博客链接
  7. 超全干货--Flink思维导图,花了3周左右编写、校对
  8. 深入JAVA 的JVM核心原理解决线上各种故障【附案例】
  9. 请谈谈你对volatile的理解?--最近小李子与面试官的一场“硬核较量”
  10. 聊聊RPC通信,经常被问到的一道面试题。源码+笔记,包懂
  11. 深入聊聊Java 垃圾回收机制【附原理图及调优方法】

欢迎扫描下方的二维码或 搜索 公众号“大数据高级架构师”,我们会有更多、且及时的资料推送给您,欢迎多多交流!

                                           

       

 

注:本文转载自blog.csdn.net的不埋雷的探长的文章"https://blog.csdn.net/weixin_32265569/article/details/108272514"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top