【设计模式之美】策略模式方法论:解耦策略的定义、创建和使用
文章目录
- 一. 策略的定义-封装策略,面向接口
- 二. 策略的创建-创建策略工厂
- 1. 对于无状态策略
- 2. 对于有状态策略
- 三. 策略的使用:动态选择
- 四. 避免分支判断-策略的优雅
- 1. 对于无状态的策略
- 2. 对于有状态的策略
策略模式是定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。
它解耦的是策略的定义、创建、使用这三部分。接下来我们看一下这三个部分的逻辑与要解决的问题
一. 策略的定义-封装策略,面向接口
策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。它是面向接口编程,所以可以灵活替换不同的策略。
public interface Strategy {void algorithmInterface();
}public class ConcreteStrategyA implements Strategy {@Overridepublic void algorithmInterface() {//具体的算法...}
}public class ConcreteStrategyB implements Strategy {@Overridepublic void algorithmInterface() {//具体的算法...}
}
二. 策略的创建-创建策略工厂
策略模式会包含一组策略,具体使用哪个策略,可以通过type来选定。我们可以把一组策略放到放到工厂类。
1. 对于无状态策略
策略模式是否有状态
如果策略类是无状态的,不包含成员变量,只是纯粹的算法实现,这样的策略对象是可以被共享使用的,不需要在每次调用 getStrategy() 的时候,都创建一个新的策略对象。即如上我们提前创建好每个策略对象,缓存到工厂类中,用的时候直接返回。
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) {... check somereturn strategies.get(type);}
}
2. 对于有状态策略
如果策略类是有状态的,每次获取工厂中获取的策略都是新的策略对象。那可以如下创建:
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;}
}
三. 策略的使用:动态选择
选定某一策略,一般根据业务逻辑,动态的选定某一个策略。
// 策略接口:EvictionStrategy
// 策略类:LruEvictionStrategy、FifoEvictionStrategy、LfuEvictionStrategy...
// 策略工厂:EvictionStrategyFactorypublic 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);//...}
}//或者直接根据type动态选择
四. 避免分支判断-策略的优雅
能够移除分支判断逻辑的模式不仅仅有策略模式,状态模式也可以。
如下判断分支:
public class OrderService {public double discount(Order order) {double discount = 0.0;OrderType type = order.getType();if (type.equals(OrderType.NORMAL)) { // 普通订单//...省略折扣计算算法代码} else if (type.equals(OrderType.GROUPON)) { // 团购订单//...省略折扣计算算法代码} else if (type.equals(OrderType.PROMOTION)) { // 促销订单//...省略折扣计算算法代码}return discount;}
}
1. 对于无状态的策略
对上面的代码重构,将不同的逻辑设计成策略类,并由工厂类来负责创建策略对象。然后通过map码表来动态选择策略。这样就没有了 if-else 分支判断语句了。
// 策略的定义
public interface DiscountStrategy {double calDiscount(Order order);
}
// 省略NormalDiscountStrategy、GrouponDiscountStrategy、PromotionDiscountStrategy类代码...// 策略的创建
public class DiscountStrategyFactory {private static final Map<OrderType, DiscountStrategy> strategies = new HashMap<>();static {strategies.put(OrderType.NORMAL, new NormalDiscountStrategy());strategies.put(OrderType.GROUPON, new GrouponDiscountStrategy());strategies.put(OrderType.PROMOTION, new PromotionDiscountStrategy());}public static DiscountStrategy getDiscountStrategy(OrderType type) {return strategies.get(type);}
}// 策略的使用
public class OrderService {public double discount(Order order) {OrderType type = order.getType();DiscountStrategy discountStrategy = DiscountStrategyFactory.getDiscountStrategy(type);return discountStrategy.calDiscount(order);}
}
2. 对于有状态的策略
对于有状态的策略,将判断逻辑放到了工厂类中
public class DiscountStrategyFactory {public static DiscountStrategy getDiscountStrategy(OrderType type) {if (type == null) {throw new IllegalArgumentException("Type should not be null.");}if (type.equals(OrderType.NORMAL)) {return new NormalDiscountStrategy();} else if (type.equals(OrderType.GROUPON)) {return new GrouponDiscountStrategy();} else if (type.equals(OrderType.PROMOTION)) {return new PromotionDiscountStrategy();}return null;}
}
参考:《设计模式之美》–王争
相关文章:
【设计模式之美】策略模式方法论:解耦策略的定义、创建和使用
文章目录 一. 策略的定义-封装策略,面向接口二. 策略的创建-创建策略工厂1. 对于无状态策略2. 对于有状态策略 三. 策略的使用:动态选择四. 避免分支判断-策略的优雅1. 对于无状态的策略2. 对于有状态的策略 策略模式是定义一族算法类,将每个…...
解析 pdfminer pdfparser.py
解析 pdfminer pdfparser.py 1. 导入必要的模块2. 定义PDFParser类2.1 初始化方法2.2 设置文档方法2.3 处理关键词方法举例说明: 3. 定义PDFStreamParser类3.1 初始化方法3.2 刷新方法3.3 处理关键词方法 总结 今天我们来看一段Python代码,这段代码实现了一个PDF文件的解析器。…...
day10:03 一文搞懂encode和encoding的区别
在Python中,处理字符串时经常会遇到encode()方法和encoding参数,它们都与字符串的编码和解码有关,但用途和上下文有所不同。下面通过案例来解释它们的关系和区别。 1. encode() 方法 encode()方法是字符串(str)类型的…...
【wordpress教程】wordpress博客网站添加非法关键词拦截
有的网站经常被恶意搜索,站长们不胜其烦。那我们如何屏蔽恶意搜索关键词呢?下面就随小编一起来解决这个问题吧。 后台设置预览图: 设置教程: 1、把以下代码添加至当前主题的 functions.php 文件中: add_action(admi…...
untiy 在菜单栏添加自定义按钮 点击按钮弹出一个Unity窗口,并在窗口里添加属性
using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityEngine.Rendering.PostProcessing;public class AutoGenerateWindow : EditorWindow //这是定义一个窗口 {public string subjecttName "科目名字";//科目的名字public GameOb…...
VIM模式之间的切换
命令行界面下,常用的文本编辑器是 VI / VIM(VI增强版),VI 是 Linux 最通用的文本编辑器,VIM相较于VI,提供了代码高亮等功能,两者用法完全兼容; 1. 进入 VIM 工作界面 vim 文件名 2. 进入编辑模式 三种方…...
Linux操作系统安全分析与防护
Linux操作系统安全机制 Linux操作系统由于其开放源代码和广泛应用,在服务器和嵌入式系统中占有重要地位。为了确保Linux系统的安全,必须了解并实施一系列有效的安全机制。这些机制包括用户身份验证、访问控制、数据加密、日志和审计、安全更新等。 一、…...
【LeetCode】面试题 16.21. 交换和
质量还不错的一道题,适合用于考察二分法。 1. 题目 2. 分析 求出两个数组的总和,我们令总和少的为less,总和多的为more;如果两个数组的总和是奇数,那么怎么都配不平,直接返回false;如果两个数…...
Web知识库应用程序LibreKB
什么是 LibreKB ? LibreKB 是一款知识库 Web 应用程序。免费、开源、自托管,基于 PHP/MySQL。 官方并没有 Docker 镜像,老苏这次图省事,并没有像往常一样构建一个镜像,而是基于 Docker 搭建了一个 LAMP 环境࿰…...
神经网络和安全结合:一种基于神经网络的智能攻击检测与防御系统;构建攻击行为预测模型
目录 神经网络和安全结合 摘要 引言 理论基础 技术实现与创新点 实验验证 结论与展望 一种基于神经网络的智能攻击检测与防御系统 一、系统概述 二、主要功能 三、技术特点 四、应用前景 构建攻击行为预测模型 一、构建攻击行为预测模型的步骤 1. 数据收集 2. …...
音视频解封装demo:将FLV文件解封装(demux)得到文件中的H264数据和AAC数据(纯手工,不依赖第三方开源库)
1、README 前言 注意:flv是不支持h.265封装的。目前解封装功能正常,所得到的H.264文件与AAC文件均可正常播放。 a. demo使用 $ make clean && make DEBUG1 $ $ $ ./flv_demux_h264_aac Usage: ./flv_demux_h264_aac avfile/test1.flv./flv_d…...
51单片机(STC8051U34K64)_RA8889_SPI4参考代码(v1.3)
硬件:STC8051U34K64 RA8889开发板(硬件跳线变更为SPI-4模式,PS101,R143,R141短接,R142不接) STC8051U34K64是STC最新推出来的单片机,主要用于替换传统的8051单片机,与标…...
关于C# 开发Winfrom事后总结
一:要求能读取文件夹中视频及图片并判断 private void Form1_Load(object sender, EventArgs e){string foldPath "路径";//获取该目录下的文件 string[] files Directory.GetFiles(foldPath "\\", "*.*", System.IO.SearchOption…...
Python学习笔记35:进阶篇(二十四)pygame的使用之音频文件播放
前言 基础模块的知识通过这么长时间的学习已经有所了解,更加深入的话需要通过完成各种项目,在这个过程中逐渐学习,成长。 我们的下一步目标是完成python crash course中的外星人入侵项目,这是一个2D游戏项目。在这之前ÿ…...
Transformer-LSTM预测 | Matlab实现Transformer-LSTM多变量时间序列预测
Transformer-LSTM预测 | Matlab实现Transformer-LSTM多变量时间序列预测 目录 Transformer-LSTM预测 | Matlab实现Transformer-LSTM多变量时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现Transformer-LSTM多变量时间序列预测,Transf…...
常见的点云数据的获取方式
1. 激光雷达(LiDAR) 获取方式:激光脉冲测距原理:激光雷达通过发射激光脉冲并接收反射信号来测量物体与传感器之间的距离。计算激光脉冲从发射到返回所需的时间,并将其转换为距离,从而生成三维点云数据。常…...
java 中钻石操作符 <> 的使用场景
在 Java 中,<> 符号通常被称为 "钻石操作符" (Diamond Operator),主要用于泛型类型的简化表示。<> 操作符引入于 Java 7,以减少泛型实例化时的冗余代码。以下是 <> 符号在 Java 中的几种主要使用场景:…...
C++ 定时器触发
c定时器,能够定时触发,即每隔一段固定时间执行一下函数 #include <iostream> #include <thread> #include <chrono> #include <signal.h> #include <time.h> #include <cstring> #include <glog/logging.h>#…...
【Docker 入门】
文章目录 概要 一、安装Docker CE1.1.配置阿里云镜像加速【可选】1.2.重启 二、Docker版本选择三、Docker指令1.Docker命令1.1.run1.2.start/stop/restart1.3.kill1.4.rm1.5.create1.6.ps1.7.exec1.8.top1.9.port 2.Dockerfile关键字3.镜像打包4.镜像运行5.镜像导入导出6.镜像查…...
现在有什么副业可以让人快速上岸?可以试试这个行业上岸其实不难
人为什么要努力赚銭? 当你想结婚的时候, 你可以慢慢挑,不着急。 当父母年老遭遇大病的时候, 你有机会尽孝。 当孩子需要时,你不会囊中羞涩。 年轻时以为金钱最重要, 如今年纪大了,发现这…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
