设计模式探索:策略模式

news2024/11/16 10:52:05

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.coloradmin.cn/o/1901028.html

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

相关文章

我使用HarmonyOs Next开发了b站的首页

1.实现效果展示&#xff1a; 2.图标准备 我使用的是iconfont图标&#xff0c;下面为项目中所使用到的图标 3. 代码 &#xff08;1&#xff09;Index.ets&#xff1a; import {InfoTop} from ../component/InfoTop import {InfoCenter} from ../component/InfoCenter import…

EtherCAT总线

目录 1、EtherCAT的系统组成 2、EtherCAT的运行原理 3、EtherCAT的数据帧结构 4、EtherCAT的寻址方式 5、EtherCAT的分布时钟 6、EtherCAT的通信模式 7、EtherCAT应用层协议 1、EtherCAT的系统组成 EtherCAT是一种实时以太网技术&#xff0c;由一个主站设备和多个从站…

SwinTransformer的相对位置索引的原理以及源码分析

文章目录 1. 理论分析2. 完整代码 引用&#xff1a;参考博客链接 1. 理论分析 根据论文中提供的公式可知是在 Q Q Q和 K K K进行匹配并除以 d \sqrt d d ​ 后加上了相对位置偏执 B B B。 A t t e n t i o n ( Q , K , V ) S o f t m a x ( Q K T d B ) V \begin{aligned} &…

认识并理解webSocket

今天逛牛客&#xff0c;看到有大佬分享说前端面试的时候遇到了关于webSocket的问题&#xff0c;一看自己都没见过这个知识点&#xff0c;赶紧学习一下&#xff0c;在此记录&#xff01; WebSocket 是一种网络通信协议&#xff0c;提供了全双工通信渠道&#xff0c;即客户端和服…

无法下载cuda

cuda下载不了 一、台式机电脑浏览器打不开cuda下载下面二、解决办法 一、台式机电脑浏览器打不开cuda下载下面 用360、chrome、Edge浏览器都打不开下载页面&#xff0c;有的人说后缀com改成cn&#xff0c;都不行。知乎上说是网络问题&#xff0c;电信换成换成移动/联通的网络会…

2229:Sumsets

网址如下&#xff1a; OpenJudge - 2229:Sumsets 这题不是我想出来的 在这里仅做记录 代码如下&#xff1a; #include<iostream> using namespace std;const int N 1000000000; int dp[1000010]; int n;int main() {cin >> n;dp[0] 1;dp[1] 1;for (int i 2…

Win11系统文件夹预览无法预览PDF文件,PDF阅读器是adobe acrobat

三步走 首先&#xff0c;打开文件夹预览功能 然后&#xff0c;设置adobe acrobat为默认PDF打开应用 最后&#xff0c;打开在Windows资源管理器中启用PDF缩略图&#xff0c;正常设定后&#xff0c;会显示配置文件&#xff0c;稍等一会。

5个实用的文章生成器,高效输出优质文章

在自媒体时代&#xff0c;优质内容的持续输出是吸引读者、提升影响力的关键。然而&#xff0c;对于许多自媒体创作者来说&#xff0c;频繁的创作难免会遭遇灵感枯竭、创作不出文章的困扰。此时&#xff0c;文章生成器便成为了得力的助手。文章生成器的优势能够快速自动生成高质…

7 系列 FPGA 引脚及封装(参考ug475)

目录 I/O BankPins引脚定义I/O and Multi-Function PinsPower Supply PinsDedicated XADC PinsTransceiver PinsDedicated Configuration PinsTemperature Sensor Pins Device 视图整个 FPGAIOBILOGIC,OLOGIC,IDELAY,ODELAYBUFIO,BUFR,IDELAYCTRLBUFMRCEBRAM,DSPIBUFDS_GTE2CLB…

Spring源码十四:Spring生命周期

上一篇我们在Spring源码十三&#xff1a;非懒加载单例Bean中看到了Spring会在refresh方法中去调用我们的finishBeanFactoryInitialization方法去实例化&#xff0c;所有非懒加载器单例的bean。并实例化后的实例放到单例缓存中。到此我们refresh方法已经接近尾声。 Spring的生命…

医疗器械企业CRM系统推荐清单(2024版)

近年来&#xff0c;我国医疗器械行业在国家政策支持、医改深入、人口老龄化和消费能力提升等因素推动下&#xff0c;得到了快速发展&#xff0c;正日益成为创新能力增强、市场需求旺盛的朝阳产业。然而&#xff0c;行业也面临价格压力、市场份额重新分配、合规风险以及产品和服…

【C语言】register 关键字

在C语言中&#xff0c;register关键字用于提示编译器将变量尽量存储在CPU的寄存器中&#xff0c;而不是在内存中。这是为了提高访问速度&#xff0c;因为寄存器的访问速度比内存快得多。使用register关键字的变量通常是频繁使用的局部变量。 基本用法 void example() {regist…

使用ChatGPT写学术论文的技巧和最佳实践指南

大家好&#xff0c;感谢关注。我是七哥&#xff0c;一个在高校里不务正业&#xff0c;折腾学术科研AI实操的学术人。关于使用ChatGPT等AI学术科研的相关问题可以和作者七哥&#xff08;yida985&#xff09;交流&#xff0c;多多交流&#xff0c;相互成就&#xff0c;共同进步&a…

ActiveMq工具之管理页面说明

文章目录 安装ActiveMQ一: 访问管理页面二: 进入管理页面&#xff0c;主页三: Queues页说明四: Topics页说明五: Subscribers页说明 安装ActiveMQ wget https://archive.apache.org/dist//activemq/5.13.3/apache-activemq-5.13.3-bin.tar.gz wget https://mirrors.huaweiclou…

ubuntu 系统中 使用docker 制作 Windows 系统,从此告别 vmware虚拟机

我的系统是 ubuntu 24 前期准备工作&#xff1a; 安装dockerdocker pull 或者 手动制作镜像 docker build 的话 必须要 科学上网&#xff0c; 好像阿里镜像都下不下来。需要 知道 docker 和docker compose 命令的使用方式 我是给docker 挂了 http代理 如果你能pull下来镜像 …

Mysql-常见DML-DQL-语句语法用法总结

1、常见DML语句 1.1 INSERT语句 说明&#xff1a;将数据插入到数据库表中。 INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...); 实例&#xff1a;添加C罗信息到数据库表中 insert into employee (ID, name, gender, entrydate, age) values …

MinIO - 从 环境搭建 -> SpringBoot实战 -> 演示,掌握 Bucket 和 Object 操作

目录 开始 Docker 部署 MinIO 中的基本概念 SpringBoot 集成 MinIO 依赖 配置 MinIO 时间差问题报错 The difference between the request time and the servers time is too large MinIO 中对 Bucket&#xff08;文件夹&#xff09; 的操作 是否存在 / 创建 查询所有…

Android 四大组件

1. Activity 应用程序中&#xff0c;一个Activity通常是一个单独的屏幕&#xff0c;它上面可以显示一些控件&#xff0c;也可以监听并对用户的事件做出响应。 Activity之间通过Intent进行通信&#xff0c;在Intent 的描述结构中&#xff0c;有两个最重要的部分&#xff1a;动…

嵌入式Linux系统编程 — 7.2 进程的环境变量

目录 1 什么是进程的环境变量 2 环境变量的作用 3 应用程序中获取环境变量 3.1 environ全局变量 3.2 获取指定环境变量 getenv 4 添加/删除/修改环境变量 4.1 putenv()函数添加环境变量 4.2 setenv()函数 4.3 unsetenv()函数 1 什么是进程的环境变量 每一个进程都有一…

Node.js 生成vue组件

在项目根目录下创建 create.js /*** 脚本生成vue组件* 主要是利用node自带的fs模块操作文件的写入* ===========================================* 准备步骤:* 1.输入作者名* 2.输入文件名* 3.输入菜单名* 4.输入文件地址* ============================================* 操…