
一个最纯粹的技术分享网站,打造精品技术编程专栏!编程进阶网
关于设计模式,所有的代码都放到了该项目。设计模式大全
策略模式是一种行为型设计模式,用于定义一系列可互换的算法,并使它们可以独立于使用它们的客户端而变化。本文档详细介绍了策略模式的基础概念、原理、结构及应用案例,包括折扣计算、文件排序等实际场景,帮助读者深入理解策略模式的实现和优势。此外,并提供了相关代码示例。适合初学者和有一定经验的开发者参考。
完成一项任务,往往可以有多种不同的方式,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成该项任务。
有许多算法可以实现某一功能,如查找、排序等,一种常用的方法是硬编码(Hard Coding)在一个类中。实现方式如下:
为了解决这些问题,可以定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法。
在这里,每一个封装算法的类我们都可以称之为策略(Strategy),为了保证这些策略的一致性,一般会用一个抽象的策略类来做算法的定义,而具体每种算法则对应于一个具体策略类。
策略模式(Strategy Pattern):更多内容
定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。
策略模式属于对象的行为模式。
其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
在以下情况下可以使用策略模式:
再继续抽象一下,策略模式的应用场景主要包括以下两种:更多内容
策略模式主要是解决避免 if-else 分支判断逻辑的吗?
实际上,这种认识是很片面的。策略模式主要的作用还是解耦策略的定义、创建和使用,控制代码的复杂度,让每个部分都不至于过于复杂、代码量过多。
除此之外,对于复杂代码来说,策略模式还能让其满足开闭原则,添加新策略的时候,最小化、集中化代码改动,减少引入 bug 的风险。
策略模式是否支持多态的使用?
通过让环境类持有一个抽象策略类(超类)的引用,在生成环境类实例对象时,让该引用指向具体的策略子类。再对应的方法调用中,就会通过Java的多态,调用对应策略子类的方法。
从而可以相互替换,不需要修改环境类内部的实现。同时,在有新的需求的情况下,也只需要修改策略类即可,降低与环境类之间的耦合度。
策略模式的重心是什么?更多内容
策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
运行时策略的唯一性:运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个。
策略模式是一种行为型设计模式,它主要用于解决算法选择问题。其主要特点在于能够将一系列算法封装起来,并让它们可以互相替换或独立于使用它的客户而变化。
假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。
一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。更多内容
比如,本网站可能对所有的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对钻石会员提供30%的促销折扣;对初级会员没有折扣。
根据描述,折扣是根据以下的几个算法中的一个进行的:
看下面代码,利用面向对象设计原则如何改进,可以保证代码后期的拓展性和解耦问题?
将不同对象分类的服务方法进行抽象,把业务逻辑的紧耦合关系拆开,实现代码的隔离保证了方便的扩展? 看看下面这段代码,改编某伟大公司产品代码,你觉得可以利用面向对象设计原则如何改进?
//将策略的定义、创建、使用直接耦合在一起。
private void calcPrice(int type,double booksPrice) {
double price;
if (type == 1) {
price = booksPrice * 0.9;
System.out.println("对中级会员提供10%的促销折扣");
} else if (type == 2) {
price = booksPrice * 0.8;
System.out.println("对高级会员提供20%的促销折扣");
} else if (type == 3) {
price = booksPrice * 0.7;
System.out.println("对钻石会员提供30%的促销折扣");
} else {
System.out.println("对初级会员没有折扣");
price = booksPrice;
}
System.out.println("图书的最终价格为:" + price);
}然后测试案例如下所示:
private void test() {
calcPrice(2,300);
}打印数据如下所示:
对高级会员提供20%的促销折扣
图书的最终价格为:240.0问题分析一下,如果再多增加几种优惠类型,那么业务就更加复杂了。你在添加if-else分支会让逻辑更加复杂,在维护和拓展这块,将会导致代码越来越臃肿!
使用策略模式来实现的结构图如下:
代码案例如下所示
利用开原则,可以尝试改造为下面的代码。更多内容将不同对象分类的服务方法进行抽象,把业务逻辑的紧耦合关系拆开,实现代码的隔离保证了方便的扩展。
抽象折扣类。这个相当于抽象策略
public interface MemberStrategy {
/**
* 计算图书的价格
* @param booksPrice 图书的原价
* @return 计算出打折后的价格
*/
double calcPrice(double booksPrice);
}初级会员折扣类。这个是具体策略1
public class PrimaryMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于初级会员的没有折扣");
return booksPrice;
}
}中级会员折扣类。这个是具体策略2
public class IntermediateMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于中级会员的折扣为10%");
return booksPrice * 0.9;
}
}高级会员折扣类。这个是具体策略3
public class AdvancedMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于高级会员的折扣为20%");
return booksPrice * 0.8;
}
}价格类。这个相当于环境角色
public class Price {
//持有一个具体的策略对象
private MemberStrategy strategy;
/**
* 构造函数,传入一个具体的策略对象
* @param strategy 具体的策略对象
*/
public Price(MemberStrategy strategy){
this.strategy = strategy;
}
/**
* 计算图书的价格
* @param booksPrice 图书的原价
* @return 计算出打折后的价格
*/
public double quote(double booksPrice){
return this.strategy.calcPrice(booksPrice);
}
}然后测试案例如下所示:
private void test() {
//选择并创建需要使用的策略对象
MemberStrategy strategy = new AdvancedMemberStrategy();
//创建环境
Price price = new Price(strategy);
//计算价格
double quote = price.quote(300);
System.out.println("图书的最终价格为:" + quote);
}打印数据如下所示:
对于高级会员的折扣为20%
图书的最终价格为:240.0策略模式实现步骤
策略模式官方实现案例如下所示
抽象策略类定义了一个算法族,其中每个算法都是一个方法。抽象策略类通过抽象方法来约束具体策略类的行为。
public interface Strategy{
void algorithm();
}具体策略类实现了抽象策略类中定义的算法,每个具体策略类都实现了一种算法。
public static class ConcreteStrategyA implements Strategy {
@Override
public void algorithm() {
System.out.println("use algorithm A");
}
}
public static class ConcreteStrategyB implements Strategy {
@Override
public void algorithm() {
System.out.println("use algorithm B");
}
}环境类持有一个抽象策略类的引用,用于调用具体的策略类中实现的算法。更多内容
public static class Context {
private final Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void algorithm() {
strategy.algorithm();
}
}测试一下,结果如下所示:
private void test() {
//创建策略
Strategy strategy = new ConcreteStrategyA();
//创建环境
Context context = new Context(strategy);
//执行行为
context.algorithm();
}策略模式包含如下角色:
策略模式结构图

策略模式时序图

业务需求:你需要在文件中排序文件内容,在案例中文件可能会很大,如果是你,你会怎么做?更多内容
思路分析:只需要将文件中的内容读取出来,并且通过逗号分割成一个一个的数字,放到内存数组中,然后编写某种排序算法(比如快排),或者直接使用编程语言提供的排序函数,对数组进行排序,最后再将数组中的数据写入文件就可以了。
用最简单直接的方式将它实现出来。更多内容
具体代码我贴在下面了,你可以先看一下。因为我们是在讲设计模式,不是讲算法,所以,在下面的代码实现中,我只给出了跟设计模式相关的骨架代码,并没有给出每种排序算法的具体代码实现。
public static class Sorter {
private static final long GB = 1000 * 1000 * 1000;
public void sortFile(String filePath) {
// 省略校验逻辑
File file = new File(filePath);
long fileSize = file.length();
if (fileSize < 6 * GB) { // [0, 6GB)
quickSort(filePath);
} else if (fileSize < 10 * GB) { // [6GB, 10GB)
externalSort(filePath);
} else if (fileSize < 100 * GB) { // [10GB, 100GB)
concurrentExternalSort(filePath);
} else { // [100GB, ~)
mapreduceSort(filePath);
}
}
private void quickSort(String filePath) {
// 快速排序
}
private void externalSort(String filePath) {
// 外部排序
}
private void concurrentExternalSort(String filePath) {
// 多线程外部排序
}
private void mapreduceSort(String filePath) {
// 利用MapReduce多机排序
}
}在“编码规范”那一部分我们讲过,函数的行数不能过多,最好不要超过一屏的大小。
所以,为了避免 sortFile() 函数过长,我们把每种排序算法从 sortFile() 函数中抽离出来,拆分成 4 个独立的排序函数。
如果只是开发一个简单的工具,那上面的代码实现就足够了。更多内容毕竟,代码不多,后续修改、扩展的需求也不多,怎么写都不会导致代码不可维护。
但是,如果我们是在开发一个大型项目,排序文件只是其中的一个功能模块,那我们就要在代码设计、代码质量上下点儿功夫了。只有每个小的功能模块都写好,整个项目的代码才能不差。
实际上,如果自己实现一下的话,你会发现,每种排序算法的实现逻辑都比较复杂,代码行数都比较多。所有排序算法的代码实现都堆在 Sorter 一个类中,这就会导致这个类的代码很多。
一个类的代码太多也会影响到可读性、可维护性。除此之外,所有的排序算法都设计成 Sorter 的私有函数,也会影响代码的可复用性。更多内容
针对上面的问题,即便我们想不到该用什么设计模式来重构,也应该能知道该如何解决,那就是将 Sorter 类中的某些代码拆分出来,独立成职责更加单一的小类。
实际上,拆分是应对类或者函数代码过多、应对代码复杂性的一个常用手段。按照这个解决思路,我们对代码进行重构。重构之后的代码如下所示:
public class SorterDesign {
private static final long GB = 1000 * 1000 * 1000;
public void sortFile(String filePath) {
// 省略校验逻辑
File file = new File(filePath);
long fileSize = file.length();
ISortAlg sortAlg;
if (fileSize < 6 * GB) { // [0, 6GB)
sortAlg = new QuickSort();
} else if (fileSize < 10 * GB) { // [6GB, 10GB)
sortAlg = new ExternalSort();
} else if (fileSize < 100 * GB) { // [10GB, 100GB)
sortAlg = new ConcurrentExternalSort();
} else { // [100GB, ~)
sortAlg = new MapReduceSort();
}
sortAlg.sort(filePath);
}
}
public interface ISortAlg {
void sort(String filePath);
}
public class QuickSort implements ISortAlg {
@Override
public void sort(String filePath) {
//...
}
}
public class ExternalSort implements ISortAlg {
@Override
public void sort(String filePath) {
//...
}
}
public class ConcurrentExternalSort implements ISortAlg {
@Override
public void sort(String filePath) {
//...
}
}
public class MapReduceSort implements ISortAlg {
@Override
public void sort(String filePath) {
//...
}
}经过拆分之后,每个类的代码都不会太多,每个类的逻辑都不会太复杂,代码的可读性、可维护性提高了。
除此之外,我们将排序算法设计成独立的类,跟具体的业务逻辑(代码中的 if-else 那部分逻辑)解耦,也让排序算法能够复用。
这一步实际上就是策略模式的第一步,也就是将策略的定义分离出来。更多内容
实际上,上面的代码还可以继续优化。
每种排序类都是无状态的,我们没必要在每次使用的时候,都重新创建一个新的对象。所以,我们可以使用工厂模式对对象的创建进行封装。
按照这个思路,我们对代码进行重构。重构之后的代码如下所示:
public class SortAlgFactory {
private static final Map<String, ISortAlg> algs = new HashMap<>();
static {
algs.put("QuickSort", new QuickSort());
algs.put("ExternalSort", new ExternalSort());
algs.put("ConcurrentExternalSort", new ConcurrentExternalSort());
algs.put("MapReduceSort", new MapReduceSort());
}
public static ISortAlg getSortAlg(String type) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type should not be empty.");
}
return algs.get(type);
}
}
public class Sorter3 {
private static final long GB = 1000 * 1000 * 1000;
public void sortFile(String filePath) {
// 省略校验逻辑
File file = new File(filePath);
long fileSize = file.length();
ISortAlg sortAlg;
if (fileSize < 6 * GB) { // [0, 6GB)
sortAlg = SortAlgFactory.getSortAlg("QuickSort");
} else if (fileSize < 10 * GB) { // [6GB, 10GB)
sortAlg = SortAlgFactory.getSortAlg("ExternalSort");
} else if (fileSize < 100 * GB) { // [10GB, 100GB)
sortAlg = SortAlgFactory.getSortAlg("ConcurrentExternalSort");
} else { // [100GB, ~)
sortAlg = SortAlgFactory.getSortAlg("MapReduceSort");
}
sortAlg.sort(filePath);
}
}经过上面两次重构之后,现在的代码实际上已经符合策略模式的代码结构了。
我们通过策略模式将策略的定义、创建、使用解耦,让每一部分都不至于太复杂。
遇到问题:每次创建新的策略类对象都需要消耗一定的时间和内存。如果我们需要多次调用同一个策略类,就会造成资源的浪费。
解决办法:使用策略类的缓存,将已经创建的策略类对象缓存起来,避免重复创建。更多内容
不过,Sorter 类中的 sortFile() 函数还是有一堆 if-else 逻辑。这里的 if-else 逻辑分支不多、也不复杂,这样写完全没问题。
但如果你特别想将 if-else 分支判断移除掉,那也是有办法的。我直接给出代码,你一看就能明白。实际上,这也是基于查表法来解决的,其中的“algs”就是“表”。
public class Sorter4 {
private static final long GB = 1000 * 1000 * 1000;
private static final List<AlgRange> algs = new ArrayList<>();
static {
algs.add(new AlgRange(0, 6*GB, SortAlgFactory.getSortAlg("QuickSort")));
algs.add(new AlgRange(6*GB, 10*GB, SortAlgFactory.getSortAlg("ExternalSort")));
algs.add(new AlgRange(10*GB, 100*GB, SortAlgFactory.getSortAlg("ConcurrentExternalSort")));
algs.add(new AlgRange(100*GB, Long.MAX_VALUE, SortAlgFactory.getSortAlg("MapReduceSort")));
}
public void sortFile(String filePath) {
// 省略校验逻辑
File file = new File(filePath);
long fileSize = file.length();
ISortAlg sortAlg = null;
for (AlgRange algRange : algs) {
if (algRange.inRange(fileSize)) {
sortAlg = algRange.getAlg();
break;
}
}
sortAlg.sort(filePath);
}
private static class AlgRange {
private long start;
private long end;
private ISortAlg alg;
public AlgRange(long start, long end, ISortAlg alg) {
this.start = start;
this.end = end;
this.alg = alg;
}
public ISortAlg getAlg() {
return alg;
}
public boolean inRange(long size) {
return size >= start && size < end;
}
}
}对于 Sorter 来说,我们可以通过同样的方法来避免修改。更多内容
我们通过将文件大小区间和算法之间的对应关系放到配置文件中。当添加新的排序算法时,我们只需要改动配置文件即可,不需要改动代码。
策略模式的优点
策略模式的缺点
在以下情况下可以使用策略模式:
策略模式与状态模式有何区别,如下所示:
策略设计模式和模板方法模式都是行为型设计模式,它们都关注于类的行为。它们的区别在于:
需求1: 假设我们正在开发一个计算器程序,该程序可以根据用户的选择进行加、减、乘、除四种运算。如果是你,你该怎么做?
具体实现可以用策略者设计模式来实现。代码可以看:StrategyDesign
需求2: 《三国演义》中刘备前往东吴联姻,诸葛亮派子龙随从,担心主公有失,便给子龙三道锦囊策略,在紧急时候依次打开。你该如何设计实现该需求。
具体实现可以用策略者模式,将三道锦囊做成策略,遇到不同场景执行。具体代码可以看:StrategyDesign
需求3: 例如你要从上海去重庆旅游,在选择交通工具时你可以选择高铁,也可以选择飞机,也可以自己开车自驾游。你要做的是从上海去重庆,在选择交通工具时面临多种选择,每一种选择就是策略。
具体代码可以看:StrategyDesign
01.策略模式基础介绍
由来介绍:完成一项任务,可以有多种不同的方式,每一种方式成为策略,可以根据环境或者条件不同完成不同任务,这个就是策略模式由来!
策略模式定义:定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。
应用场景:
策略模式主要是为了解决 if-else 分支判断逻辑的吗?其实除了实现同样功能,重点在于解耦合,让每种策略单独实现,降低代码复杂度,添加或者移除策略时不影响老代码。
策略模式是一种行为型设计模式,它主要用于解决算法选择问题。其主要特点在于能够将一系列算法封装起来,并让它们可以互相替换或独立于使用它的客户而变化。
02.策略模式原理
根据描述,折扣是根据以下的几个算法中的一个进行的:
第一种实现方式:使用if-else分支实现该业务。如果再多增加几种优惠类型,那么业务就更加复杂了。让逻辑更加复杂,在维护和拓展这块,将会导致代码越来越臃肿!
第二种实现方式:抽象出折扣接口,然后分别创建初级会员策略类,中级会员策略类,高级会员策略类,钻石会员策略类。然后根据环境使用不同策略完善折扣汇算的功能。符合开闭原则,方便拓展,好维护!
03.策略模式结构
策略模式包含如下角色:
05.策略者模式分析
策略模式的优点在于降低系统的耦合度,提高系统的灵活性和扩展性。缺点在于增加了系统的复杂性和理解难度。
模块 | 描述 | 备注 |
|---|---|---|
GitHub | 多个YC系列开源项目,包含Android组件库,以及多个案例 | |
博客汇总 | 汇聚Java,Android,C/C++,网络协议,算法,编程总结等 | |
设计模式 | 六大设计原则,23种设计模式,设计模式案例,面向对象思想 | |
Java进阶 | 数据设计和原理,面向对象核心思想,IO,异常,线程和并发,JVM | |
网络协议 | 网络实际案例,网络原理和分层,Https,网络请求,故障排查 | |
计算机原理 | 计算机组成结构,框架,存储器,CPU设计,内存设计,指令编程原理,异常处理机制,IO操作和原理 | |
学习C编程 | C语言入门级别系统全面的学习教程,学习三到四个综合案例 | |
C++编程 | C++语言入门级别系统全面的教学教程,并发编程,核心原理 | |
算法实践 | 专栏,数组,链表,栈,队列,树,哈希,递归,查找,排序等 | |
Android | 基础入门,开源库解读,性能优化,Framework,方案设计 |
23种设计模式
23种设计模式 & 描述 & 核心作用 | 包括 |
|---|---|
创建型模式 提供创建对象用例。能够将软件模块中对象的创建和对象的使用分离 | 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
结构型模式 关注类和对象的组合。描述如何将类或者对象结合在一起形成更大的结构 | 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) |
行为型模式 特别关注对象之间的通信。主要解决的就是“类或对象之间的交互”问题 | 责任链模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。