【设计模式之美】策略模式方法论:解耦策略的定义、创建和使用
文章目录
- 一. 策略的定义-封装策略,面向接口
- 二. 策略的创建-创建策略工厂
- 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.镜像查…...
现在有什么副业可以让人快速上岸?可以试试这个行业上岸其实不难
人为什么要努力赚銭? 当你想结婚的时候, 你可以慢慢挑,不着急。 当父母年老遭遇大病的时候, 你有机会尽孝。 当孩子需要时,你不会囊中羞涩。 年轻时以为金钱最重要, 如今年纪大了,发现这…...
3步掌握城通网盘解析工具:彻底告别30秒等待与限速困扰
3步掌握城通网盘解析工具:彻底告别30秒等待与限速困扰 【免费下载链接】ctfileGet 获取城通网盘一次性直连地址 项目地址: https://gitcode.com/gh_mirrors/ct/ctfileGet 还在为城通网盘下载的漫长等待和蜗牛般的速度而烦恼吗?城通网盘作为国内广…...
MLT框架的“Producer”到底有多智能?深入loader.dict与avformat揭秘媒体文件自动解析
MLT框架的“Producer”智能解析机制:从loader.dict到avformat的深度探索 当你在MLT框架中写下Producer(profile, nullptr, "video.mp4")这样一行看似简单的代码时,背后其实隐藏着一套精妙的媒体文件自动解析系统。这个系统能够根据文件扩展名、…...
别再只刷固件了!深入Proxmark3硬件层:AT91SAM7S512芯片与Bootrom.bin的救砖原理详解
深入Proxmark3硬件层:AT91SAM7S512芯片与Bootrom.bin的救砖原理详解 当你的Proxmark3设备突然"四灯全亮",USB连接失效,变成一块"砖头"时,大多数教程只会告诉你"短接测试点,用J-Link烧录bootr…...
别再只用DS18B20了!用51单片机和ADC0804做个PT100温度计,从硬件接线到代码调试全流程
从DS18B20到PT100:用51单片机打造工业级温度监测系统 在嵌入式开发领域,温度测量是一个永恒的话题。当大多数初学者还停留在使用DS18B20这类数字温度传感器时,工业领域早已广泛采用PT100铂电阻作为温度测量的主力军。本文将带你跨越数字传感器…...
别再手动画图了!用Graphviz + Python自动生成流程图,效率提升10倍
用PythonGraphviz实现自动化图表生成:告别低效手绘时代 你是否曾在PPT中反复调整箭头位置,只为让一张流程图看起来更专业?或是花半小时拖拽图形,却发现某个节点的颜色需要全局修改?在技术文档、系统架构设计或算法可视…...
保姆级教程:用ESP32 AT固件实现手机蓝牙配对,从编译到连接一次搞定
ESP32蓝牙开发实战:从固件编译到手机配对的完整指南 在物联网设备开发中,蓝牙连接是最基础也最常用的功能之一。ESP32作为一款高性价比的Wi-Fi/蓝牙双模芯片,凭借其出色的性能和丰富的开发资源,已经成为智能家居、可穿戴设备等领域…...
重新定义开源协作:GitHub中文界面如何突破语言认知边界
重新定义开源协作:GitHub中文界面如何突破语言认知边界 【免费下载链接】github-chinese GitHub 汉化插件,GitHub 中文化界面。 (GitHub Translation To Chinese) 项目地址: https://gitcode.com/gh_mirrors/gi/github-chinese GitHub中文汉化插件…...
CSPM 信息与文档管理:从混沌到数智化,企业转型的核心命门
在 2026 年 CSPM 最新考纲中,信息与文档管理从边缘考点升级为战略级核心模块,直指企业数字化转型的最大盲区 ——文档混沌、信息孤岛、数据资产流失。本文以犀利视角拆解传统文档管理的致命弊端,结合 AI 大模型、区块链存证、BIM 数字孪生、知…...
深入解析ACP Bridge:构建高效微服务通信与数据同步的协议转换桥梁
1. 项目概述与核心价值最近在折腾一个跨平台数据同步的项目,遇到了一个挺有意思的组件——allvegetable/acp-bridge。乍一看这个名字,可能会有点摸不着头脑,acp是什么?bridge又在这里扮演什么角色?实际上,这…...
CircuitPython嵌入式开发实战:内存管理、BLE通信与异步编程优化
1. 项目概述:CircuitPython开发中的核心挑战与应对思路 在嵌入式硬件开发领域,CircuitPython以其对Python语法的友好支持,极大地降低了硬件编程的门槛。然而,从桌面环境转向资源极度受限的微控制器(MCU)世界…...
