欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > 【设计模式】【行为型模式】策略模式(Strategy)

【设计模式】【行为型模式】策略模式(Strategy)

2025/2/14 2:04:20 来源:https://blog.csdn.net/flzjcsg3/article/details/145536816  浏览:    关键词:【设计模式】【行为型模式】策略模式(Strategy)

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

文章目录

  • 一、入门
    • 什么是策略模式?
    • 为什么需要策略模式?
    • 怎样实现策略模式?
  • 二、策略模式在源码中的运用
    • 2.1、Java Collections 中的排序策略
    • 2.2、Spring 中的资源加载策略
  • 三、总结
  • 参考

一、入门

什么是策略模式?

策略模式是一种行为设计模式,允许在运行时选择算法或行为。它将算法封装在独立的类中,使得它们可以互换,而不影响客户端代码。

为什么需要策略模式?

策略模式的主要目的是解决算法或行为在代码中硬编码的问题,使得系统更加灵活、可扩展和易于维护。可以优化大量的if-else。

怎样实现策略模式?

策略模式的主要角色如下:
抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略所需的接口。
具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为
环境(Context)类:持有一个策略类的引用,最终给客户端调用。

【案例】促销活动
百货公司,在不同的节日,会有不同的促销
在这里插入图片描述

定义百货公司所有促销活动的共同接口

public interface Strategy {void show();
}

定义具体策略角色(Concrete Strategy):每个节日具体的促销活动

//为春节准备的促销活动A
public class StrategyA implements Strategy {public void show() {System.out.println("买一送一");}
}
//为中秋准备的促销活动B
public class StrategyB implements Strategy {public void show() {System.out.println("满200元减50元");}
}
//为圣诞准备的促销活动C
public class StrategyC implements Strategy {public void show() {System.out.println("满1000元加一元换购任意200元以下商品");}
}
定义环境角色(Context):用于连接上下文,即把促销活动推销给客户,这里可以理解为销售员
public class SalesMan {            //持有抽象策略角色的引用               private Strategy strategy;        public SalesMan(Strategy strategy) {   this.strategy = strategy;       }                     //向客户展示促销活动                public void salesManShow(){        strategy.show();           }                     
} 

二、策略模式在源码中的运用

2.1、Java Collections 中的排序策略

Java的Collections.sort()方法使用了策略模式来实现排序功能。它允许通过传递不同的Comparator实现来定义不同的排序策略。

List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);// 策略1:升序排序
Collections.sort(numbers, new Comparator<Integer>() {@Overridepublic int compare(Integer a, Integer b) {return a.compareTo(b);}
});
System.out.println("升序排序: " + numbers);// 策略2:降序排序
Collections.sort(numbers, new Comparator<Integer>() {@Overridepublic int compare(Integer a, Integer b) {return b.compareTo(a);}
});
System.out.println("降序排序: " + numbers);

在上面的代码中Comparator是策略接口。具体的排序逻辑(升序、降序)是策略实现。Collections.sort()是上下文,负责调用策略。
源码中,Collections类的sort方法,里面调用了Arrays类的sort方法。ArraysTimSort类的sort方法

// Collections类
public static <T> void sort(List<T> list, Comparator<? super T> c) {list.sort(c);
}default void sort(Comparator<? super E> c) {Object[] a = this.toArray();Arrays.sort(a, (Comparator) c);    // 调用Arrays类的sort方法ListIterator<E> i = this.listIterator();for (Object e : a) {i.next();i.set((E) e);}
}// Arrays类
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); // 调用TimSort类的sort方法}
}

TimSort类中,这里我们只要关注,我们传的策略,入参c的使用地方就好了。这里会调用countRunAndMakeAscending方法,我们关注这个方法,我们传的排序策略,也就是入参c,会被使用。

static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,T[] work, int workBase, int workLen) {assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;int nRemaining  = hi - lo;if (nRemaining < 2)return;  // Arrays of size 0 and 1 are always sorted// If array is small, do a "mini-TimSort" with no mergesif (nRemaining < MIN_MERGE) {int initRunLen = countRunAndMakeAscending(a, lo, hi, c);  // 关注调用这个方法
...// countRunAndMakeAscending 方法
private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,Comparator<? super T> c) {assert lo < hi;int runHi = lo + 1;if (runHi == hi)return 1;if (c.compare(a[runHi++], a[lo]) < 0) { // 排序策略被使用while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)runHi++;reverseRange(a, lo, runHi);} else {                          while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)runHi++;}return runHi - lo;
}

2.2、Spring 中的资源加载策略

Spring框架中的ResourceLoader接口和其实现类(如ClassPathResourceLoaderFileSystemResourceLoader等)也使用了策略模式。

ResourceLoader resourceLoader = new DefaultResourceLoader();// 策略1:从类路径加载资源
Resource classPathResource = resourceLoader.getResource("classpath:application.properties");
System.out.println("类路径资源: " + classPathResource.exists());// 策略2:从文件系统加载资源
Resource fileSystemResource = resourceLoader.getResource("file:/path/to/file.txt");
System.out.println("文件系统资源: " + fileSystemResource.exists());

ResourceLoader是策略接口。具体的资源加载逻辑(类路径、文件系统等)是策略实现。DefaultResourceLoader是上下文,负责调用策略。
下面是结合源码说明
策略接口:ResourceLoader 接口

public interface ResourceLoader {Resource getResource(String location);
}

具体策略:ClassPathResource、FileSystemResource等是具体的策略实现。

// ClassPathResource实现
public class ClassPathResource extends AbstractFileResolvingResource {private final String path;public ClassPathResource(String path) {this.path = path;}@Overridepublic InputStream getInputStream() throws IOException {InputStream is = getClassLoader().getResourceAsStream(path);if (is == null) {throw new FileNotFoundException("Resource not found: " + path);}return is;}
}// FileSystemResource
public class FileSystemResource extends AbstractResource {private final File file;public FileSystemResource(String path) {this.file = new File(path);}@Overridepublic InputStream getInputStream() throws IOException {return new FileInputStream(file);}
}

上下文:DefaultResourceLoader 是上下文,负责根据路径选择合适的策略。

public class DefaultResourceLoader implements ResourceLoader {@Overridepublic Resource getResource(String location) {// 根据路径前缀选择策略if (location.startsWith(CLASSPATH_URL_PREFIX)) {return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));}else if (location.startsWith(FILE_URL_PREFIX)) {return new FileSystemResource(location.substring(FILE_URL_PREFIX.length()));}else {try {// 尝试作为URL加载URL url = new URL(location);return new UrlResource(url);}catch (MalformedURLException ex) {// 如果都不是,默认为文件系统资源return new FileSystemResource(location);}}}
}

这里应该有UU们会好奇,诶,为什么具体策略没有实现策略接口?Spring 的资源加载策略是策略模式的一种变体。它与经典策略模式的区别在于:

  • 经典策略模式:
    • 策略接口和具体策略是直接实现的。
    • 例如,排序策略中,Comparator 是策略接口,具体的排序类是策略实现。
  • Spring 的资源加载策略:
    • 策略接口(ResourceLoader)和具体策略(ClassPathResource 等)之间通过上下文(DefaultResourceLoader)连接。
    • 具体策略实现的是 Resource 接口,而不是 ResourceLoader 接口。

三、总结

策略模式通过将算法或行为封装到独立的类中,提供了一种灵活、可扩展的方式来管理代码中的变化部分。它的核心优势是解耦动态切换,但也会带来类的数量增加和客户端使用成本的问题。适用于需要动态切换行为、避免重复代码或隔离算法实现细节的场景。

优点
灵活性:允许在运行时动态切换算法或行为,无需修改客户端代码。
可扩展性:新增策略时只需添加新的策略类,符合开闭原则(对扩展开放,对修改关闭)。
解耦:将算法或行为与使用它的上下文分离,降低了代码的耦合度。
避免重复代码:将相似的算法提取到独立的策略类中,减少代码重复。
易于测试:每个策略类可以独立测试,简化了测试过程。

缺点
增加类的数量:每个策略都需要一个独立的类,可能会导致类的数量增多,增加系统复杂性。
客户端需要了解策略:客户端必须知道有哪些策略,并选择合适的策略,增加了使用成本。
性能开销:在运行时切换策略可能会引入额外的性能开销(如对象创建和销毁)

适用场景
需要动态切换算法或行为:例如,支付方式、排序算法、资源加载策略等。
有多个相似的类,只有行为不同:例如,不同类型的折扣计算、不同的日志记录方式等。
避免使用复杂的条件语句:当代码中有大量if-else或switch-case语句时,可以用策略模式替代。
需要隔离算法的实现细节:当不希望暴露算法的实现细节,或者希望算法可以独立变化时。
需要对算法进行扩展:当系统需要支持新的算法,且不希望修改现有代码时。

参考

黑马程序员Java设计模式详解, 23种Java设计模式(图解+框架源码分析+实战)_哔哩哔哩_bilibili

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com