设计模式探索:策略模式

1. 什么是策略模式(Strategy Pattern)

定义

策略模式(Strategy Pattern)的原始定义是:定义一系列算法,将每一个算法封装起来,并使它们可以相互替换。策略模式让算法可以独立于使用它的客户端而变化。

目的

策略模式的目的是在软件开发中,当实现某一个功能存在多种算法或者策略时,可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。
比如网购,你可以选择工商银行、农业银行、建设银行等等,但是它们提供的算法都是一致的,就是帮你付款。
在这里插入图片描述

角色

策略模式的主要角色如下:

  1. 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  2. 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
  3. 环境或上下文(Context)类:是使用算法的角色,持有一个策略类的引用,最终给客户端调用。

UML类图

在策略模式中可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里每一个封装算法的类都可以被称为一种策略,为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做算法的声明.而每种算法对应一个具体的策略类。
在这里插入图片描述

实现代码

// 抽象策略类
public interface Strategy {void algorithm();
}// 具体策略类A
public class ConcreteStrategyA implements Strategy {@Overridepublic void algorithm() {System.out.println("执行策略A");}
}// 具体策略类B
public class ConcreteStrategyB implements Strategy {@Overridepublic void algorithm() {System.out.println("执行策略B");}
}// 环境类
public class Context {// 维持一个对抽象策略类的引用private Strategy strategy;public Context(Strategy strategy) {this.strategy = strategy;}// 调用策略类中的算法public void algorithm() {strategy.algorithm();}
}// 客户端代码
public class Client {public static void main(String[] args) {Strategy strategyA = new ConcreteStrategyA();Context context = new Context(strategyA); // 可以在运行时指定类型context.algorithm();}
}

2.优缺点

优点

  1. 易于扩展和维护:由于不同的算法被封装在不同的类中,所以我们可以很容易地添加新的算法或修改已有算法,而不需要修改客户端的代码。
  2. 提高代码的可读性:将不同的算法封装在不同的类中,使得代码更加模块化,易于理解和维护。
  3. 消除大量的条件语句:使用策略模式,我们可以将不同的算法替换成不同的类,从而消除大量的if-else语句,使得代码更加简洁和易于理解。

缺点

  1. 需要额外的类和接口:使用策略模式,我们需要为每个算法都创建一个独立的类,从而增加了代码的复杂度。
  2. 客户端需要知道所有的策略类:使用策略模式,客户端需要知道所有的策略类,以便在运行时选择合适的策略。这可能会增加代码的复杂度。

应用场景

策略模式适用于以下场景:

  1. 需要根据不同的条件选择不同的算法时:例如,计算器程序需要根据用户输入的运算符选择相应的计算方法。
  2. 需要在运行时动态地选择算法时:例如,某个系统需要根据用户的配置或环境变量来选择合适的算法。
  3. 需要将算法的实现细节与客户端代码分离时:例如,某个系统需要根据不同的数据来源来解析数据,但是客户端并不关心数据的解析细节。

总结

策略模式通过定义一系列算法,将每一个算法封装起来,并使它们可以相互替换,从而让算法可以独立于使用它的客户端而变化。通过使用策略模式,可以提高代码的扩展性、可读性和维护性,同时也可以消除大量的条件语句。

在工作中,为了消除代码中的大量 if-else 语句并提升代码的可维护性和扩展性,可以使用策略模式。下面是详细的实现步骤和代码示例。

3.如何用设计模式消除代码中的ifelse(你在工作中使用过哪些设计模式)

不使用设计模式

这是一个请假审批流程的代码示例,包含员工类、请假单类和审核类,直接使用 if-else 语句来处理不同的审批规则。

public class Employee {private String name; // 姓名private int level;   // 级别: P6, P7, P8// Constructor, getters and setters
}public class LeaveForm {private Employee employee; // 员工private String reason;     // 请假原因private int days;          // 天数private int type;          // 类型: 0-病假, 1-婚丧假, 2-年假// Constructor, getters and setters
}public class LeaveService {public void audit(LeaveForm leaveForm) {// 3天以下婚丧假, 自动通过if (leaveForm.getDays() <= 3 && leaveForm.getType() == 1) {System.out.println("三天以下婚丧假 无需审批自动通过!");}// 3天以上婚丧假else if (leaveForm.getDays() > 3 && leaveForm.getType() == 1) {System.out.println("三天以上婚丧假 进入上级审批流程!");}// 总经理请假else if (leaveForm.getEmployee().getLevel() == 9) {System.out.println("总经理请假无需审批自动通过!");}// 一天病假else if (leaveForm.getDays() == 1 && leaveForm.getType() == 0) {System.out.println("一天病假无需审批自动通过!");}// 一天以上病假else if (leaveForm.getDays() > 1 && leaveForm.getType() == 0) {System.out.println("一天以上病假进入审批流程!");}}
}

使用策略模式进行优化

通过策略模式,将所有的 if-else 分支的业务逻辑抽取为各种策略类,判断条件和执行逻辑封装到对应的策略类中,让客户端去依赖策略接口,保证具体策略类的改变不影响客户端。

策略接口
public interface AuditStrategy {boolean isSupport(LeaveForm leaveForm);void audit(LeaveForm leaveForm);int getPriority();String getName();
}
具体策略类
public class AuditStrategyImpl_1 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getDays() <= 3 && leaveForm.getType() == 1;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("三天以下婚丧假 无需审批自动通过!");}@Overridepublic int getPriority() {return 0;}@Overridepublic String getName() {return "三天以下婚假审批规则";}
}public class AuditStrategyImpl_2 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getDays() > 3 && leaveForm.getType() == 1;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("三天以上婚丧假 进入上级审批流程!");}@Overridepublic int getPriority() {return 0;}@Overridepublic String getName() {return "三天以上婚丧假审批规则";}
}public class AuditStrategyImpl_3 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getEmployee().getLevel() == 9;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("总经理请假无需审批自动通过!");}@Overridepublic int getPriority() {return 999;}@Overridepublic String getName() {return "总经理请假审批规则";}
}
策略工厂
public class AuditStrategyFactory {private final static AuditStrategyFactory factory = new AuditStrategyFactory();private List<AuditStrategy> auditStrategyList = new ArrayList<>();private AuditStrategyFactory() {auditStrategyList.add(new AuditStrategyImpl_1());auditStrategyList.add(new AuditStrategyImpl_2());auditStrategyList.add(new AuditStrategyImpl_3());// Add more strategies here}public static AuditStrategyFactory getInstance() {return factory;}public AuditStrategy getAuditStrategy(LeaveForm leaveForm) {AuditStrategy auditStrategy = null;for (AuditStrategy strategy : auditStrategyList) {if (strategy.isSupport(leaveForm)) {if (auditStrategy == null || strategy.getPriority() > auditStrategy.getPriority()) {auditStrategy = strategy;}}}if (auditStrategy == null) {throw new RuntimeException("没有匹配到请假审核规则");}return auditStrategy;}
}
业务类
public class LeaveServiceNew {public void audit(LeaveForm leaveForm) {AuditStrategyFactory factory = AuditStrategyFactory.getInstance();AuditStrategy strategy = factory.getAuditStrategy(leaveForm);strategy.audit(leaveForm);}
}
测试
public class Client {public static void main(String[] args) {LeaveServiceNew leaveServiceNew = new LeaveServiceNew();LeaveForm form1 = new LeaveForm(new Employee("李总经理", 9), "甲流发烧", 10, 0);leaveServiceNew.audit(form1);LeaveForm form2 = new LeaveForm(new Employee("打工人1", 2), "甲流发烧", 2, 0);leaveServiceNew.audit(form2);LeaveForm form3 = new LeaveForm(new Employee("打工人2", 3), "结婚", 2, 1);leaveServiceNew.audit(form3);LeaveForm form4 = new LeaveForm(new Employee("打工人3", 4), "请年假,休息休息", 5, 2);leaveServiceNew.audit(form4);}
}
添加新规则

如果需要添加新的年假规则,只需要创建新的策略类并添加到工厂中即可。

public class AuditStrategyImpl_6 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getType() == 2;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("查询您的剩余年假天数...");System.out.println("剩余年假还有6天, 进入审批流程");}@Overridepublic int getPriority() {return 0;}@Overridepublic String getName() {return "年假审批规则";}
}

在工厂类中添加新的策略:

private AuditStrategyFactory() {auditStrategyList.add(new AuditStrategyImpl_1());auditStrategyList.add(new AuditStrategyImpl_2());auditStrategyList.add(new AuditStrategyImpl_3());auditStrategyList.add(new AuditStrategyImpl_6()); // 新添加的年假规则// Add more strategies here
}

通过这种方式,已经成功消除了 if-else 结构,每当新来了一种请假规则,只需要添加新的规则处理策略,并修改工厂中的集合。如果要使得程序符合开闭原则,可以通过反射机制,动态地加载策略类。

使用反射机制动态加载策略类
public class AuditStrategyFactory {private final static AuditStrategyFactory factory = new AuditStrategyFactory();private List<AuditStrategy> auditStrategyList = new ArrayList<>();private AuditStrategyFactory() {// 动态加载策略类try {String packageName = "com.example.strategies"; // 策略类所在包名ClassLoader classLoader = Thread.currentThread().getContextClassLoader();String path = packageName.replace('.', '/');Enumeration<URL> resources = classLoader.getResources(path);List<File> dirs = new ArrayList<>();while (resources.hasMoreElements()) {URL resource = resources.nextElement();dirs.add(new File(resource.getFile()));}for (File directory : dirs) {auditStrategyList.addAll(findClasses(directory, packageName));}} catch (Exception e) {e.printStackTrace();}}private List<AuditStrategy> findClasses(File directory, String packageName) throws ClassNotFoundException, InstantiationException, IllegalAccessException {List<AuditStrategy> strategies = new ArrayList<>();if (!directory.exists()) {return strategies;}File[] files = directory.listFiles();for (File file : files) {if (file.isDirectory()) {strategies.addAll(findClasses(file, packageName + "." + file.getName()));} else if (file.getName().endsWith(".class")) {String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);Class<?> clazz = Class.forName(className);if (AuditStrategy.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {strategies.add((AuditStrategy) clazz.newInstance());}}}return strategies;}public static AuditStrategyFactory getInstance() {return factory;}public AuditStrategy getAuditStrategy(LeaveForm leaveForm) {AuditStrategy auditStrategy = null;for (AuditStrategy strategy : auditStrategyList) {if (strategy.isSupport(leaveForm)) {if (auditStrategy == null || strategy.getPriority() > auditStrategy.getPriority()) {auditStrategy = strategy;}}}if (auditStrategy == null) {throw new RuntimeException("没有匹配到请假审核规则");}return auditStrategy;}
}

通过这种方式,策略类可以动态地从指定包中加载,实现了真正的开闭原则。

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

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

相关文章

打卡第4天----链表

通过学习基础,发现我的基本功还得需要再练练,思路得再更加清晰明了,这样子做算法题才能驾轻就熟。每天记录自己的进步。 一、两两交换 题目编号:24 题目描述: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本…

[数据结构] 基于交换的排序 冒泡排序快速排序

标题&#xff1a;[数据结构] 基于交换的排序 冒泡排序&&快速排序 水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 &#xff08;一&#xff09;冒泡排序 优化后实现&#xff1a; &#xff08;二&#xff09;快速排序 I、实现方法&#xff1a; &#…

opencv环境搭建-python

最近遇到了一些图像处理的需求&#xff0c;所以需要学习一下opencv,来记录一下我的学习历程。 安装numpy pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy安装matplotlib pip install -i https://pypi.tuna.tsinghua.edu.cn/simple matplotlib安装opencv …

ctfshow web入门 web338--web344

web338 原型链污染 comman.js module.exports {copy:copy };function copy(object1, object2){for (let key in object2) {if (key in object2 && key in object1) {copy(object1[key], object2[key])} else {object1[key] object2[key]}}}login.js var express …

【ubuntu】挂载新磁盘

1、查看磁盘 sudo fdisk -l#Disk /dev/sdb: 4.0 TiB #Disk model: HNA641010BCF105 #Units: sectors of 1 * 512 512 bytes #Sector size (logical/physical): 512 bytes / 4096 bytes #I/O size (minimum/optimal): 4096 bytes / 4096 bytes #Disklabel type: gpt #Disk id…

python argparse模块nargs用法

nargs 是 argparse 模块中用来指定参数的数量的属性。不同的 nargs 取值有不同的含义&#xff0c;下面是一些常用的用法&#xff1a; nargsNone (默认值)&#xff1a;表示该参数只能接收一个值。例如&#xff1a;--foo 123。 nargs?&#xff1a;表示该参数最多接收一个值。如…

gcc/g++的四步编译

目录 前言1.预处理&#xff08;进行宏替换&#xff09;2.编译&#xff08;生成汇编&#xff09;3.汇编&#xff08;生成二进制文件&#xff09;4. 链接 &#xff08;生成可执行文件&#xff09;a. 动态库 && 动态链接b. 静态库 && 静态链接c. 验证d. 动静态链接…

技术实现路径怎么写?(Word项目技术路径文档参考)

软件项目编写技术实现路径至关重要&#xff0c;因为它为项目团队提供了清晰的开发蓝图。这一路径明确了从项目启动到交付各阶段所需的技术方案、步骤及预期成果&#xff0c;有助于团队统一认识&#xff0c;确保开发工作有序进行。同时&#xff0c;技术实现路径有助于识别潜在的…

HetuEngine简介

目录 HetuEngine是什么&#xff1f; HetuEngine的特点以及使用场景 特点 使用场景 HetuEngine介绍 结构 近期用到了Hetu&#xff0c;了解下这个工具是起什么作用的。 HetuEngine是什么&#xff1f; 是引擎&#xff0c;设计是为了让与当前的大数据生态完美融合的引擎&am…

本安防爆手机:危险环境下的安全通信解决方案

在石油化工、煤矿、天然气等危险环境中&#xff0c;通信安全是保障工作人员生命安全和生产顺利进行的关键。防爆智能手机作为专为这些环境设计的通信工具&#xff0c;提供了全方位的安全通信解决方案。 防爆设计与材料&#xff1a; 防爆智能手机采用特殊的防爆结构和材料&…

Mysql部署MHA高可用

部署前准备&#xff1a; mysql-8.0.27下载地址&#xff1a;https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.27-1.el7.x86_64.rpm-bundle.tar mha-manager下载地址&#xff1a;https://github.com/yoshinorim/mha4mysql-manager/releases/download/v0.58/mha4mysql-mana…

【Selenium】 使用save_screenshot截图无法保存图片

Selenium 使用save_screenshot截图无法保存 代码如下 from time import sleep from selenium import webdriver driver webdriver.Chrome() driver.maximize_window() driver.get(http://www.baidu.com) # 截取当前窗口&#xff0c;指定截图图片的保存位置 driver.save_scre…

为什么需要做网络安全服务?

网络安全服务之所以重要&#xff0c;是因为它在保护数字资产、维护企业运营、确保法规遵从、防范恶意行为以及建立信任等方面扮演着关键角色。以下是一些主要的理由&#xff1a; 保护核心资产和数据&#xff1a; 数字化转型使得企业数据变得极其宝贵&#xff0c;包括知识产权、…

深度学习模型加密python版本

支持加密的模型: # torch、torch script、onnx、tensorrt 、torch2trt、tensorflow、tensorflow2tensorrt、paddlepaddle、paddle2tensorrt 深度学习推理模型通常以文件的形式进行保存&#xff0c;相应的推理引擎通过读取模型文件并反序列化即可进行推理过程. 这样一来&#…

数据库——事务管理

title: 数据库——事务管理 date: 2024-07-06 11:55:39 tags: 数据库 categories: 数据库 cover: /image/T1.jpg description: 数据库的事务管理的相关知识 事务管理 事务管理是对一系列数据库操作进行管理的过程&#xff0c;这些操作被视为一个不可分割的工作单元&#xff0…

20K Stars!一个轻量级的 JS 库

大家好,我是CodeQi! 一位热衷于技术分享的码仔。 Driver.js 是一个轻量级的 JavaScript 库,旨在帮助开发人员创建网站或应用程序的引导和教程。通过 Driver.js,您可以引导用户了解网站的各个功能和使用方式。 Driver.js 提供了高度可定制的功能,使其能够适应各种需求和…

宝塔-Linux模板常用命令-centos7

一、宝塔-Linux模板常用命令&#xff1a; 1.停止宝塔 /etc/init.d/bt stop 2.启动宝塔 /etc/init.d/bt start 3.重启宝塔 /etc/init.d/bt restart 4.卸载宝塔 /etc/init.d/bt stop && chkconfig --del bt && rm -f /etc/init.d/bt && rm -rf …

如何使用echart做K线图

使用ECharts制作K线图需要先引入ECharts的库文件&#xff0c;然后通过调用相应的API来配置和渲染K线图。以下是一个简单的示例代码&#xff1a; // 引入ECharts库文件 <script src"https://cdn.jsdelivr.net/npm/echarts5.0.0/dist/echarts.min.js"></scri…

使用Python绘制和弦图

使用Python绘制和弦图 和弦图效果代码 和弦图 和弦图用于展示数据的多对多关系&#xff0c;适合用于社交网络、交通流量等领域的分析。 效果 代码 import pandas as pd import holoviews as hv from holoviews import opts hv.extension(bokeh)# 示例数据 data [(A, B, 2),…

想在vue中预览doxc,excel,pdf文件? vue-office提供包支持

在浩瀚的Vue生态中&#xff0c;vue-office犹如一颗璀璨的星辰&#xff0c;以其独特的魅力照亮了开发者处理多种文件格式的预览之路。这款精心打造的Vue组件库&#xff0c;不仅拥抱了Vue2的经典&#xff0c;也紧密跟随Vue3的步伐&#xff0c;展现了卓越的技术前瞻性和兼容性。它…