当前位置: 首页 > news >正文

【设计模式之美】策略模式方法论:解耦策略的定义、创建和使用

文章目录

  • 一. 策略的定义-封装策略,面向接口
  • 二. 策略的创建-创建策略工厂
    • 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;}
}

 

参考:《设计模式之美》–王争

相关文章:

【设计模式之美】策略模式方法论:解耦策略的定义、创建和使用

文章目录 一. 策略的定义-封装策略&#xff0c;面向接口二. 策略的创建-创建策略工厂1. 对于无状态策略2. 对于有状态策略 三. 策略的使用&#xff1a;动态选择四. 避免分支判断-策略的优雅1. 对于无状态的策略2. 对于有状态的策略 策略模式是定义一族算法类&#xff0c;将每个…...

解析 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中&#xff0c;处理字符串时经常会遇到encode()方法和encoding参数&#xff0c;它们都与字符串的编码和解码有关&#xff0c;但用途和上下文有所不同。下面通过案例来解释它们的关系和区别。 1. encode() 方法 encode()方法是字符串&#xff08;str&#xff09;类型的…...

【wordpress教程】wordpress博客网站添加非法关键词拦截

有的网站经常被恶意搜索&#xff0c;站长们不胜其烦。那我们如何屏蔽恶意搜索关键词呢&#xff1f;下面就随小编一起来解决这个问题吧。 后台设置预览图&#xff1a; 设置教程&#xff1a; 1、把以下代码添加至当前主题的 functions.php 文件中&#xff1a; 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模式之间的切换

命令行界面下&#xff0c;常用的文本编辑器是 VI / VIM(VI增强版)&#xff0c;VI 是 Linux 最通用的文本编辑器&#xff0c;VIM相较于VI&#xff0c;提供了代码高亮等功能&#xff0c;两者用法完全兼容&#xff1b; 1. 进入 VIM 工作界面 vim 文件名 2. 进入编辑模式 三种方…...

Linux操作系统安全分析与防护

Linux操作系统安全机制 Linux操作系统由于其开放源代码和广泛应用&#xff0c;在服务器和嵌入式系统中占有重要地位。为了确保Linux系统的安全&#xff0c;必须了解并实施一系列有效的安全机制。这些机制包括用户身份验证、访问控制、数据加密、日志和审计、安全更新等。 一、…...

【LeetCode】面试题 16.21. 交换和

质量还不错的一道题&#xff0c;适合用于考察二分法。 1. 题目 2. 分析 求出两个数组的总和&#xff0c;我们令总和少的为less&#xff0c;总和多的为more&#xff1b;如果两个数组的总和是奇数&#xff0c;那么怎么都配不平&#xff0c;直接返回false&#xff1b;如果两个数…...

Web知识库应用程序LibreKB

什么是 LibreKB &#xff1f; LibreKB 是一款知识库 Web 应用程序。免费、开源、自托管&#xff0c;基于 PHP/MySQL。 官方并没有 Docker 镜像&#xff0c;老苏这次图省事&#xff0c;并没有像往常一样构建一个镜像&#xff0c;而是基于 Docker 搭建了一个 LAMP 环境&#xff0…...

神经网络和安全结合:一种基于神经网络的智能攻击检测与防御系统;构建攻击行为预测模型

目录 神经网络和安全结合 摘要 引言 理论基础 技术实现与创新点 实验验证 结论与展望 一种基于神经网络的智能攻击检测与防御系统 一、系统概述 二、主要功能 三、技术特点 四、应用前景 构建攻击行为预测模型 一、构建攻击行为预测模型的步骤 1. 数据收集 2. …...

音视频解封装demo:将FLV文件解封装(demux)得到文件中的H264数据和AAC数据(纯手工,不依赖第三方开源库)

1、README 前言 注意&#xff1a;flv是不支持h.265封装的。目前解封装功能正常&#xff0c;所得到的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)

硬件&#xff1a;STC8051U34K64 RA8889开发板&#xff08;硬件跳线变更为SPI-4模式&#xff0c;PS101&#xff0c;R143&#xff0c;R141短接&#xff0c;R142不接&#xff09; STC8051U34K64是STC最新推出来的单片机&#xff0c;主要用于替换传统的8051单片机&#xff0c;与标…...

关于C# 开发Winfrom事后总结

一&#xff1a;要求能读取文件夹中视频及图片并判断 private void Form1_Load(object sender, EventArgs e){string foldPath "路径";//获取该目录下的文件 string[] files Directory.GetFiles(foldPath "\\", "*.*", System.IO.SearchOption…...

Python学习笔记35:进阶篇(二十四)pygame的使用之音频文件播放

前言 基础模块的知识通过这么长时间的学习已经有所了解&#xff0c;更加深入的话需要通过完成各种项目&#xff0c;在这个过程中逐渐学习&#xff0c;成长。 我们的下一步目标是完成python crash course中的外星人入侵项目&#xff0c;这是一个2D游戏项目。在这之前&#xff…...

Transformer-LSTM预测 | Matlab实现Transformer-LSTM多变量时间序列预测

Transformer-LSTM预测 | Matlab实现Transformer-LSTM多变量时间序列预测 目录 Transformer-LSTM预测 | Matlab实现Transformer-LSTM多变量时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现Transformer-LSTM多变量时间序列预测&#xff0c;Transf…...

常见的点云数据的获取方式

1. 激光雷达&#xff08;LiDAR&#xff09; 获取方式&#xff1a;激光脉冲测距原理&#xff1a;激光雷达通过发射激光脉冲并接收反射信号来测量物体与传感器之间的距离。计算激光脉冲从发射到返回所需的时间&#xff0c;并将其转换为距离&#xff0c;从而生成三维点云数据。常…...

java 中钻石操作符 <> 的使用场景

在 Java 中&#xff0c;<> 符号通常被称为 "钻石操作符" (Diamond Operator)&#xff0c;主要用于泛型类型的简化表示。<> 操作符引入于 Java 7&#xff0c;以减少泛型实例化时的冗余代码。以下是 <> 符号在 Java 中的几种主要使用场景&#xff1a…...

C++ 定时器触发

c定时器&#xff0c;能够定时触发&#xff0c;即每隔一段固定时间执行一下函数 #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.镜像查…...

现在有什么副业可以让人快速上岸?可以试试这个行业上岸其实不难

人为什么要努力赚銭&#xff1f; 当你想结婚的时候&#xff0c; 你可以慢慢挑&#xff0c;不着急。 当父母年老遭遇大病的时候&#xff0c; 你有机会尽孝。 当孩子需要时&#xff0c;你不会囊中羞涩。 年轻时以为金钱最重要&#xff0c; 如今年纪大了&#xff0c;发现这…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...