设计模式探索:策略模式

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

定义

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

目的

策略模式的目的是在软件开发中,当实现某一个功能存在多种算法或者策略时,可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。

比如网购,你可以选择工商银行、农业银行、建设银行等等,但是它们提供的算法都是一致的,就是帮你付款。

角色

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

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

UML类图

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

实现代码

java 复制代码
// 抽象策略类
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 语句来处理不同的审批规则。

java 复制代码
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 分支的业务逻辑抽取为各种策略类,判断条件和执行逻辑封装到对应的策略类中,让客户端去依赖策略接口,保证具体策略类的改变不影响客户端。

策略接口
java 复制代码
public interface AuditStrategy {
    boolean isSupport(LeaveForm leaveForm);
    void audit(LeaveForm leaveForm);
    int getPriority();
    String getName();
}
具体策略类
java 复制代码
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 "总经理请假审批规则";
    }
}
策略工厂
java 复制代码
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;
    }
}
业务类
java 复制代码
public class LeaveServiceNew {
    public void audit(LeaveForm leaveForm) {
        AuditStrategyFactory factory = AuditStrategyFactory.getInstance();
        AuditStrategy strategy = factory.getAuditStrategy(leaveForm);
        strategy.audit(leaveForm);
    }
}
测试
java 复制代码
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);
    }
}
添加新规则

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

java 复制代码
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 "年假审批规则";
    }
}

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

java 复制代码
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 结构,每当新来了一种请假规则,只需要添加新的规则处理策略,并修改工厂中的集合。如果要使得程序符合开闭原则,可以通过反射机制,动态地加载策略类。

使用反射机制动态加载策略类
java 复制代码
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;
    }
}

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

相关推荐
In_life 在生活9 小时前
设计模式(四)装饰器模式与命令模式
设计模式
瞎姬霸爱.9 小时前
设计模式-七个基本原则之一-接口隔离原则 + SpringBoot案例
设计模式·接口隔离原则
鬣主任10 小时前
Spring设计模式
java·spring boot·设计模式
程序员小海绵【vincewm】12 小时前
【设计模式】结合Tomcat源码,分析外观模式/门面模式的特性和应用场景
设计模式·tomcat·源码·外观模式·1024程序员节·门面模式
丶白泽12 小时前
重修设计模式-行为型-命令模式
设计模式·命令模式
gjh120816 小时前
设计模式:工厂方法模式和策略模式
设计模式·工厂方法模式·策略模式
shinelord明17 小时前
【再谈设计模式】抽象工厂模式~对象创建的统筹者
数据结构·算法·设计模式·软件工程·抽象工厂模式
前端拾光者18 小时前
前端开发设计模式——责任链模式
设计模式·责任链模式
liang899919 小时前
设计模式之策略模式(Strategy)
设计模式·策略模式
马剑威(威哥爱编程)20 小时前
读写锁分离设计模式详解
java·设计模式·java-ee