设计模式探索:策略模式

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

定义

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

目的

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

角色

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

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

UML类图

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

实现代码

// 抽象策略类
public interface Strategy {
    void algorithm();
}

// 具体策略类A
public class ConcreteStrategyA implements Strategy {
    @Override
    public void algorithm() {
        System.out.println("执行策略A");
    }
}

// 具体策略类B
public class ConcreteStrategyB implements Strategy {
    @Override
    public 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 {
    @Override
    public boolean isSupport(LeaveForm leaveForm) {
        return leaveForm.getDays() <= 3 && leaveForm.getType() == 1;
    }

    @Override
    public void audit(LeaveForm leaveForm) {
        System.out.println(leaveForm);
        System.out.println("三天以下婚丧假 无需审批自动通过!");
    }

    @Override
    public int getPriority() {
        return 0;
    }

    @Override
    public String getName() {
        return "三天以下婚假审批规则";
    }
}

public class AuditStrategyImpl_2 implements AuditStrategy {
    @Override
    public boolean isSupport(LeaveForm leaveForm) {
        return leaveForm.getDays() > 3 && leaveForm.getType() == 1;
    }

    @Override
    public void audit(LeaveForm leaveForm) {
        System.out.println(leaveForm);
        System.out.println("三天以上婚丧假 进入上级审批流程!");
    }

    @Override
    public int getPriority() {
        return 0;
    }

    @Override
    public String getName() {
        return "三天以上婚丧假审批规则";
    }
}

public class AuditStrategyImpl_3 implements AuditStrategy {
    @Override
    public boolean isSupport(LeaveForm leaveForm) {
        return leaveForm.getEmployee().getLevel() == 9;
    }

    @Override
    public void audit(LeaveForm leaveForm) {
        System.out.println(leaveForm);
        System.out.println("总经理请假无需审批自动通过!");
    }

    @Override
    public int getPriority() {
        return 999;
    }

    @Override
    public 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 {
    @Override
    public boolean isSupport(LeaveForm leaveForm) {
        return leaveForm.getType() == 2;
    }

    @Override
    public void audit(LeaveForm leaveForm) {
        System.out.println(leaveForm);
        System.out.println("查询您的剩余年假天数...");
        System.out.println("剩余年假还有6天, 进入审批流程");
    }

    @Override
    public int getPriority() {
        return 0;
    }

    @Override
    public 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.mfbz.cn/a/779366.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity 使用AVProMovieCapture实现Game视图屏幕录制

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity 使用AVProMovieCapture实现Game视图屏幕录制 TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、心…

01背包问题-队列分支限界法-C++

0-1背包问题-队列分支限界法 问题描述&#xff1a; 给定n种物品和一个背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包中的物品&#xff0c;使得装入背包中物品的总价值最大&#xff1f;对于给定的n种物品的重量和价值&#xff0c;以及背包的容量&…

17-JS封装:工具类方法

目录 一、extend方法 二、添加一些工具类方法&#xff1a;$.xxx() 实现1&#xff1a; 实现2&#xff1a; 一、extend方法 jQuery.fn.extend jQuery.extend function(...args){let target,source[];source[...args];//判断2种情况 //$.extend({}) -->给$添加属性//$.…

算法金 | 欧氏距离算法、余弦相似度、汉明、曼哈顿、切比雪夫、闵可夫斯基、雅卡尔指数、半正矢、Sørensen-Dice

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 抱个拳&#xff0c;送个礼 在算法模型构建中&#xff0c;我们经常需要计算样本之间的相似度&#xff0c;通常的做法是计算样本之间的距…

源码航行阅读目录

&#x1f3c0; 前言 在准备面试和学习的过程中&#xff0c;我阅读了比较多的源码&#xff0c;比如 JUC、Spring、MyBatis&#xff0c;收获了很多代码的设计思想&#xff0c;也对平时调用的 API 有了更深入的理解&#xff1b;但过多散乱的笔记给我的整理复习带来了比较大的麻烦…

基于Java技术的人事管理系统

你好&#xff0c;我是专注于计算机科学领域的小野。如果你对人事管理系统感兴趣或有相关需求&#xff0c;欢迎私信交流。 开发语言&#xff1a; Java 数据库&#xff1a; MySQL 技术&#xff1a; B/S模式、Java技术、SpringBoot 工具&#xff1a; Eclipse、MySQL、浏览…

【linux学习---1】点亮一个LED是多么的困难!!!

文章目录 1、原理图找对应引脚2、IO复用3、IO配置4、GPIO配置5、GPIO时钟使能6、总结7、编程8、编译9、链接10、格式转换11、反汇编&#xff08;查看用&#xff09;12、使用Makefile操作13、代码烧写14、代码验证 1、原理图找对应引脚 从上图 可以看出&#xff0c; 蜂鸣器 接到…

Photoshop属于什么软件 Photoshop缓存文件清理 Mac清理PS缓存 苹果电脑ps内存满了怎么清理

对于所有热爱使用Adobe Photoshop的Mac用户来说&#xff0c;这款软件无疑是创意工作的强大助手。但是&#xff0c;随着时间的积累&#xff0c;你可能会发现Photoshop开始变得有点慢&#xff0c;反应迟钝。这通常是因为Photoshop的缓存和临时文件堆积&#xff0c;占用了宝贵的系…

刷题之买股票的最佳时机(leetcode)

买股票的最佳时机 动态规划入门题。 最简单的模拟式解法&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {//也可以换一种思路&#xff0c;因为只交易一次&#xff0c;那么找出股票最便宜的时候买入&#xff0c;最贵的时候卖出&#xff…

每日一题~ (判断是否是合法的出栈序列)

大概的题意&#xff1a; 将 1-n 按照顺序进栈&#xff0c;问 输入的序列是否是合法的出栈序列。 遍历序列&#xff0c;如果当前这个值a小于 栈顶的值&#xff0c;说明它还未进栈&#xff08;因为我们是按照顺序进栈的&#xff09;&#xff0c;所以我们将 一些元素进栈&#xff…

uniapp 封装请求

新建request文件夹 下新建index.js 和index.js 或者创建units文件放入index.js 和api文件夹放入index.js(api.js)//看公司规范 1. index.js // 全局请求封装 // const base_url http://localhost:8080/devapi var base_url process.env.NODE_ENV development ? http://…

Studying-代码随想录训练营day27| 贪心算法理论基础、455.分发饼干、376.摆动序列、53.最大子序和

第27天&#xff0c;贪心开始&#xff01;(ง •_•)ง&#x1f4aa;&#xff0c;编程语言&#xff1a;C 目录 贪心算法理论基础 贪心的套路 贪心的一般解题步骤 总结 455.分发饼干 376.摆动序列 53.最大子序和 总结 贪心算法理论基础 什么是贪心&#xff1f;—— 贪…

自动化设备上位机设计 三

目录 一 设计原型 二 后台源码 一 设计原型 二 后台源码 using SqlSugar;namespace 自动化上位机设计 {public partial class Form1 : Form{SqlHelper sqlHelper new SqlHelper();SqlSugarClient dbContent null;bool IsRun false;int Count 0;public Form1(){Initializ…

SpringBoot新手快速入门系列教程五:基于JPA的一个Mysql简单读写例子

现在我们来做一个简单的读写Mysql的项目 1&#xff0c;先新建一个项目&#xff0c;我们叫它“HelloJPA”并且添加依赖 2&#xff0c;引入以下依赖&#xff1a; Spring Boot DevTools (可选&#xff0c;但推荐&#xff0c;用于开发时热部署)Lombok&#xff08;可选&#xff0c…

Google Earth Engine(GEE)——在控制台打印出来所选区域的缩略图

结果 函数 ui.Thumbnail(image, params, onClick, style) A fixed-size thumbnail image generated asynchronously from an ee.Image. Arguments: image (Image, optional): The ee.Image from which to generate the thumbnail. Defaults to an empty ee.Image. param…

简单分享下python多态

目录&#xff1a; 一、多态是啥嘞&#xff08;龙生九子各有不同&#xff0c;这就是多态&#xff09; 二、基础的实例 三、多态的优势与应用场景 四、深入理解 一、多态是啥嘞&#xff08;龙生九子各有不同&#xff0c;这就是多态&#xff09; 多态&#xff08;Polymorphism&…

Laravel5+mycat 报错 “Packets out of order”

背景 近期对负责项目&#xff0c;配置了一套 主从复制的 MySQL 集群 使用了中间件 mycat 但测试发现&#xff0c;替换了原来的数据连接后&#xff0c;会出现 Packets out of order 的报错 同时注意到&#xff0c;有的框架代码中竟然也会失效&#xff0c;比如 controller 类中&…

Mac电脑iTerm2 如何设置无限滑动

1.打开iTerm2应用 2.打开偏好设置 3.选中Profiles -> Terminal 4.选择Unlimited scrollback

Linux开发讲课33---线程实现与线程控制步骤简析

线程概述 进程是系统中程序执行和资源分配的基本单位。 每个进程都拥有自己的数据段、代码段和堆栈段&#xff0c;这就造成了进程在进行切换等操作时都需要有比较负责的上下文切换等动作。为了进一步减少处理机的空转时间支持多处理器和减少上下文切换开销&#xff0c;进程在演…

Ollama+OpenWeb UI搭建最简单的大模型交互界面

Open WebUI是一个专为大型语言模型&#xff08;LLMs&#xff09;设计的Web用户界面。这个界面提供了一个直观、响应迅速且易于使用的平台&#xff0c;使用户能够与本地运行的语言模型进行交互&#xff0c;就像与云服务中的模型交互一样。可以非常方便的调试、调用本地模型。你能…