设计模式-策略模式 Strategy

策略模式

  • 1) 原理和实现
    • 1、策略的定义
    • 2、策略的创建
    • 3、策略的使用

该模式最常见的应用场景是,利用它来避免冗长的 if-else 或 switch 分支判断。不过,它的作用还不止如此。它也可以像模板模式那样,提供框架的扩展点等等。

1) 原理和实现

策略模式,英文全称是 Strategy Design Pattern。该模式是这样定义的:

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it。

翻译成中文就是:定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。

策略模式主要包含以下角色:

  1. 策略接口(Strategy):定义所有支持的算法的公共接口。客户端使用这个接口与具体策略进行交互。
  2. 具体策略(Concrete Strategy):实现策略接口的具体策略类。这些类封装了实际的算法逻辑。
  3. 上下文(Context):持有一个策略对象,用于与客户端进行交互。上下文可以定义一些接口,让客户端不直接与策略接口交互,从而实现策略的封装。

让我们以一个简单的例子来说明策略模式:假设我们要实现一个计算器,支持加法、减法和乘法运算。我们可以使用策略模式将各种运算独立为不同的策略,并让客户端根据需要选择和使用不同的策略。

首先,定义一个策略接口Operation

public interface Operation {double execute(double num1, double num2);
}

接下来,创建具体策略类来实现加法、减法和乘法运算:

public class Addition implements Operation {@Overridepublic double execute(double num1, double num2) {return num1 + num2;}
}public class Subtraction implements Operation {@Overridepublic double execute(double num1, double num2) {return num1 - num2;}
}public class Multiplication implements Operation {@Overridepublic double execute(double num1, double num2) {return num1 * num2;}
}

然后,创建一个上下文类Calculator,让客户端可以使用这个类来执行不同的运算:

public class Calculator {// 客户端可以持有一个算法private Operation operation;public void setOperation(Operation operation) {this.operation = operation;}public double calculate(double num1, double num2) {return operation.execute(num1, num2);}
}

现在,客户端可以使用Calculator类来执行不同的运算,例如:

public class Client {public static void main(String[] args) {// 定义一个调用方Calculator calculator = new Calculator();// 计算加法Operation operation = new Addition();calculator.setOperation(operation);double calculate = calculator.calculate(10, 12.9);System.out.println("calculate = " + calculate);// 计算减法operation = new Subtraction();calculator.setOperation(operation);calculate = calculator.calculate(10, 12.9);System.out.println("calculate = " + calculate);}
}

在这个例子中,我们使用策略模式将加法、减法和乘法运算独立为不同的策略。客户端可以根据需要选择和使用不同的策略。Calculator上下文类持有一个Operation策略对象,并通过setOperation方法允许客户端设置所需的策略。这种方式使得算法的选择和执行更加灵活,易于扩展和维护。

策略模式的优点包括:

  1. 提高代码的可维护性和可扩展性。当需要添加新的算法时,我们只需要实现一个新的具体策略类,而无需修改客户端代码。
  2. 符合开闭原则。策略模式允许我们在不修改现有代码的情况下引入新的策略。
  3. 避免使用多重条件判断。使用策略模式可以消除一些复杂的条件判断语句,使代码更加清晰和易于理解。

策略模式的缺点包括:

  1. 客户端需要了解所有的策略。为了选择合适的策略,客户端需要了解不同策略之间的区别。
  2. 增加了类的数量。策略模式会导致程序中具体策略类的数量增加,这可能会导致代码的复杂性增加。

在实际开发中,我们可以根据业务需求和系统架构灵活地运用策略模式。例如,在电商系统中,我们可以使用策略模式处理不同的促销策略;在游戏系统中,我们可以使用策略模式处理不同的角色行为等。

1、策略的定义

策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。因为所有的策略类都实现相同的接口,所以,客户端代码基于接口而非实现编程,可以灵活地替换不同的策略。示例代码如下所示:

public interface Strategy {void algorithmInterface();
}
public class ConcreteStrategyA implements Strategy {@Overridepublic void  algorithmInterface() {//具体的算法...}
}
public class ConcreteStrategyB implements Strategy {@Overridepublic void  algorithmInterface() {//具体的算法...}
}

2、策略的创建

因为策略模式会包含一组策略,在使用它们的时候,一般会通过类型(type)来判断创建哪个策略来使用。为了封装创建逻辑,我们需要对客户端代码屏蔽创建细节。

事实上我们可以做一定的优化,可以把根据 type 创建策略的逻辑抽离出来,放到工厂类中。示例代码如下所示:

public class StrategyFactory {private static final Map<String, Strategy> strategies = new HashMap<>();static {strategies.put("A", new ConcreteStrategyA());strategies.put("B", new ConcreteStrategyB());}public static Strategy getStrategy(String type) {if (type == null || type.isEmpty()) {throw new IllegalArgumentException("type should not be empty.");}return strategies.get(type);}
}

一般来讲,如果策略类是无状态的,不包含成员变量,只是纯粹的算法实现,这样的策略对象是可以被共享使用的,不需要在每次调用 getStrategy() 的时候,都创建一个新的策略对象。针对这种情况,我们可以使用上面这种工厂类的实现方式,事先创建好每个策略对象,缓存到工厂类中,用的时候直接返回。

相反,如果策略类是有状态的,根据业务场景的需要,我们希望每次从工厂方法中,获得的都是新创建的策略对象,而不是缓存好可共享的策略对象,那我们就需要按照如下方式来实现策略工厂类。

public class StrategyFactory {public static Strategy getStrategy(String type) {if (type == null || type.isEmpty()) {throw new IllegalArgumentException("type should not be empty.");}if (type.equals("A")) {return new ConcreteStrategyA();} else if (type.equals("B")) {return new ConcreteStrategyB();}return null;}
}

3、策略的使用

刚刚讲了策略的定义和创建,现在,我们再来看一下,策略的使用。

我们知道,策略模式包含一组可选策略,客户端代码一般如何确定使用哪个策略呢?最常见的是运行时动态确定使用哪种策略,这也是策略模式最典型的应用场景。

这里的“运行时动态”指的是,我们事先并不知道会使用哪个策略,而是在程序运行期间,根据配置、用户输入、计算结果等这些不确定因素,动态决定使用哪种策略。接下来,我们通过一个例子来解释一下。

// 策略接口:EvictionStrategy
// 策略类:LruEvictionStrategy、FifoEvictionStrategy、LfuEvictionStrategy...
// 策略工厂:EvictionStrategyFactory
public class UserCache {private Map<String, User> cacheData = new HashMap<>();private EvictionStrategy eviction;public UserCache(EvictionStrategy eviction) {this.eviction = eviction;}//...
}
// 运行时动态确定,根据配置文件的配置决定使用哪种策略
public class Application {public static void main(String[] args) throws Exception {EvictionStrategy evictionStrategy = null;Properties props = new Properties();props.load(new FileInputStream("./config.properties"));String type = props.getProperty("eviction_type");evictionStrategy = EvictionStrategyFactory.getEvictionStrategy(type);UserCache userCache = new UserCache(evictionStrategy);//...}
}
// 非运行时动态确定,在代码中指定使用哪种策略
public class Application {public static void main(String[] args) {//...EvictionStrategy evictionStrategy = new LruEvictionStrategy();UserCache userCache = new UserCache(evictionStrategy);//...}
}

从上面的代码中,我们也可以看出,“非运行时动态确定”,也就是第二个 Application 中的使用方式,并不能发挥策略模式的优势。在这种应用场景下,策略模式实际上退化成了“面向对象的多态特性”或“基于接口而非实现编程原则”,这其实就是开篇时列举的例子的场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/681721.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux基础命令1

Linux基础命令1 持续更新 ‍ 命令参数和作用示例ls列出文件和文件夹当前 ls​指定 ls /home​递归 ls -R​单列 ls -1​逗号分隔 ls -m​显示隐藏 ls -a​显示类型 ls -F​显示权限、所有者等详细信息 ls -l​按字母反序排列 ls -r​按扩展名排序 ls -lX​按日期和时间排序 …

php数组与字符串函数

php数组与字符串函数 1. php数组2. 字符串函数 1. php数组 在php中&#xff0c;有三种类型的数组&#xff1a; 数值数组 - 带有数字ID键的数组关联数组 - 带有指定的键的数组&#xff0c;每个键关联一个值多维数组 - 包含一个或多个数组的数组 2. 字符串函数 在PHP中&#xf…

耳机壳UV树脂制作私模定制耳塞适合什么样的人使用呢?

耳机壳UV树脂制作私模定制耳塞适合以下人群使用&#xff1a; 对音质要求高的人&#xff1a;私模定制耳塞能够完美契合用户的耳朵形状&#xff0c;减少漏音和外部噪音的干扰&#xff0c;提供更好的音质体验。需要长时间佩戴耳机的人&#xff1a;私模定制耳塞能够提高佩戴舒适度…

<网络安全>《30 常用安全标准》

《常用安全标准》 1 个人信息安全 o《信息安全技术 个人信息安全规范》&#xff08;GB/T35273-2017) o《信息安全技术 个人信息去标识化指南》&#xff08;GB/T37964-2019) 2 工业控制安全 o《信息安全技术 工业控制系统安全检查指南》&#xff08;GB/T 37980-2019) o《信息…

C++ //练习 6.41 下面的哪个调用是非法的?为什么?哪个调用虽然合法但显然与程序员的初衷不符?为什么?

C Primer&#xff08;第5版&#xff09; 练习 6.41 练习 6.41 下面的哪个调用是非法的&#xff1f;为什么&#xff1f;哪个调用虽然合法但显然与程序员的初衷不符&#xff1f;为什么&#xff1f; char *init(int ht, int wd 80, char bckgrnd ); (a) init(); (b) init(24…

Git基础命令,分支,标签的使用【快速入门Git】

Git基础命令&#xff0c;分支&#xff0c;标签的使用【快速入门Git】 Git基础常用命令Git工作流程工作区&#xff0c;暂存区和版本库文件状态获取Git仓库 git init | git clone查看文件状态 git status暂存已修改的文件 git add 查看已暂存和未暂存的修改 git diff提交文件更改…

【JAVA】类加载机制

目录 一、快速梳理JAVA类加载机制 1、JDK8的类加载体系 2、沙箱保护机制 3、Linking链接过程 二、一个用类加载机制加薪的故事 三、通过类加载器引入外部Jar包 四、自定义类加载器实现Class代码混淆 五、自定义类加载器实现热加载 六、打破双亲委派&#xff0c;实现同类…

VBA技术资料MF118:在多个工作表中插入页眉和页脚

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

多模态论文串讲·下【论文精读·49】最近使用 transformer encoder 和 decoder 的一些方法

大家好&#xff0c;我们今天就接着上次多模态串讲&#xff0c;来说一说最近使用 transformer encoder 和 decoder 的一些方法。 1 BLIP&#xff1a;Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation 我们要过的第一篇论文…

计算机网络——11EMail

EMail 电子邮件&#xff08;EMail&#xff09; 3个主要组成部分 用户代理邮件服务器简单邮件传输协议&#xff1a;SMTP 用户代理 又名“邮件阅读器”撰写、编辑和阅读邮件输入和输出邮件保存在服务器上 邮件服务器 邮箱中管理和维护发送给用户的邮件输出报文队列保持待发…

LeetCode、435. 无重叠区间【中等,贪心 区间问题】

文章目录 前言LeetCode、435. 无重叠区间【中等&#xff0c;贪心 区间问题】题目链接及分类思路贪心、区间问题 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技…

08:K8S资源对象管理|服务与负载均衡|Ingress

K8S资源对象管理&#xff5c;服务与负载均衡&#xff5c;Ingress DaemonSet控制器污点策略容忍容忍污点 其他资源对象Job资源对象 有限生命周期CronJob资源对象 集群服务服务自动发现headless服务 实现服务定位与查找 服务类型 Ingress插件 发布服务的方式 DaemonSet控制器 Da…

【BFS拓扑排序】207. 课程表

207. 课程表 解题思路 首先构建了一个 inDegree 哈希表&#xff0c;用于存储每门课程的入度&#xff0c;即有多少课程依赖当前课程。 构建了一个 adj 哈希表&#xff0c;用于存储每门课程所依赖的其他课程。这个结构可以理解为一个邻接表&#xff0c;对于每门课程&#xff0c…

Elasticsearch:适用于 iOS 和 Android 本机应用程序的 Elastic APM

作者&#xff1a;来自 Elastic Akhilesh Pokhariyal, Cesar Munoz, Bryce Buchanan 适用于本机应用程序的 Elastic APM 提供传出 HTTP 请求和视图加载的自动检测&#xff0c;捕获自定义事件、错误和崩溃&#xff0c;并包括用于数据分析和故障排除目的的预构建仪表板。 适用于 …

【北邮鲁鹏老师计算机视觉课程笔记】08 texture 纹理表示

【北邮鲁鹏老师计算机视觉课程笔记】08 texture 纹理表示 1 纹理 规则和不规则的 2 纹理的用处 从纹理中恢复形状 3 分割与合成 4 分析纹理进行分类 通过识别纹理分析物理性质 如何区分纹理 5 寻找有效的纹理分类方法 发现模式、描述区域内模式 A对应图2 B对应图…

Java 基于微信小程序的电子商城购物系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

从零开始学howtoheap:理解fastbins的​large_bin攻击

how2heap是由shellphish团队制作的堆利用教程&#xff0c;介绍了多种堆利用技术&#xff0c;后续系列实验我们就通过这个教程来学习。环境可参见从零开始配置pwn环境&#xff1a;从零开始配置pwn环境&#xff1a;从零开始配置pwn环境&#xff1a;优化pwn虚拟机配置支持libc等指…

EL表达式和JSTL标签

1.1. EL表达式概述 EL&#xff08;Expression Language&#xff09;是一门表达式语言&#xff0c;它对应<%…%>。我们知道在JSP中&#xff0c;表达式会被输出&#xff0c;所以EL表达式也会被输出。 EL表达式的格式&#xff1a;${…}&#xff0c;例如&#xff1a;${12}…

【深度学习】S2 数学基础 P1 线性代数(上)

目录 基本数学对象标量与变量向量矩阵张量降维求和非降维求和累计求和 点积与向量积点积矩阵-向量积矩阵-矩阵乘法 深度学习的三大数学基础 —— 线性代数、微积分、概率论&#xff1b; 自本篇博文以下几遍博文&#xff0c;将对这三大数学基础进行重点提炼。 本节博文将介绍线…

mysql Day05

sql性能分析 sql执行频率 show global status like Com_______ 慢查询日志 执行时间超过10秒的sql语句 profile详情 show profiles帮助我们了解时间都耗费到哪里了 #查看每一条sql的耗时情况 show profiles#查看指定query_id的sql语句各个阶段的耗时情况 show profile fo…