写在前面

        写业务代码时,最常见的“坏味道”就是满屏的if-else。判断用户类型、计算折扣、选择支付方式……每加一种新情况,就往if-else里塞一个分支。三个月后,这个函数动辄几百行,改一处怕崩十处。更可怕的是,不同分支里还夹杂着相似的逻辑,复制粘贴成了常态。这就是典型的面向过程思维写Java。今天我们要聊的策略模式,就是专门干掉if-else的利器。再配合工厂模式,连switch都不用手写。读完这篇,你将理解什么叫“对扩展开放,对修改关闭”,以及为什么说这才是真正的Java面向对象编程。

一、什么是策略模式?

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

核心角色

  • 策略接口(Strategy):定义所有策略的公共方法。

  • 具体策略(ConcreteStrategy):实现策略接口,封装具体算法或规则。

  • 上下文(Context):持有策略对象的引用,负责调用策略。

二、为什么需要策略模式?——一个if-else的反面案例

假设我们有一个计算订单折扣的功能:

public double calculateDiscount(String userLevel, double amount) {
    if ("VIP".equals(userLevel)) {
        return amount * 0.8;   // VIP 8折
    } else if ("GOLD".equals(userLevel)) {
        return amount * 0.9;   // 黄金 9折
    } else if ("SILVER".equals(userLevel)) {
        return amount * 0.95;  // 白银 95折
    } else {
        return amount;         // 普通无折扣
    }
}

痛点

  1. 每增加一种用户等级,就要修改这个函数,违反开闭原则

  2. 折扣算法可能在其他地方复用,只能复制粘贴。

  3. 单元测试需要覆盖所有分支,复杂且易遗漏。

  4. 多个if-else嵌套时,代码可读性极差。

策略模式的解决方案:将每种折扣算法封装成独立的策略类,客户端根据需要选择策略。

三、策略模式的代码实现(Java)

3.1 定义策略接口

java

public interface DiscountStrategy {
    double calculate(double amount);
}

3.2 实现具体策略

public class VipDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculate(double amount) {
        return amount * 0.8;
    }
}

public class GoldDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculate(double amount) {
        return amount * 0.9;
    }
}

public class SilverDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculate(double amount) {
        return amount * 0.95;
    }
}

public class RegularDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculate(double amount) {
        return amount;
    }
}

3.3 上下文类

public class OrderContext {
    private DiscountStrategy strategy;
    
    public void setStrategy(DiscountStrategy strategy) {
        this.strategy = strategy;
    }
    
    public double calculateDiscount(double amount) {
        return strategy.calculate(amount);
    }
}

3.4 客户端使用

public class Client {
    public static void main(String[] args) {
        OrderContext order = new OrderContext();
        
        // VIP用户
        order.setStrategy(new VipDiscountStrategy());
        System.out.println(order.calculateDiscount(100)); // 80.0
        
        // 黄金用户
        order.setStrategy(new GoldDiscountStrategy());
        System.out.println(order.calculateDiscount(100)); // 90.0
    }
}

问题:客户端仍然需要知道具体策略类并手动创建。这没有完全消除if-else——客户端还得判断等级然后new对应的策略。如何解决?引入工厂模式

四、为什么要引入工厂模式?

工厂模式负责根据条件创建对应的策略对象,将“选择哪个策略”的逻辑集中到工厂中,客户端只需传入标识(如字符串、枚举),工厂返回策略实例。

4.1 简单工厂 + 策略模式

public class DiscountStrategyFactory {
    public static DiscountStrategy getStrategy(String userLevel) {
        switch (userLevel) {
            case "VIP":
                return new VipDiscountStrategy();
            case "GOLD":
                return new GoldDiscountStrategy();
            case "SILVER":
                return new SilverDiscountStrategy();
            default:
                return new RegularDiscountStrategy();
        }
    }
}

客户端变为:

String level = "VIP";
DiscountStrategy strategy = DiscountStrategyFactory.getStrategy(level);
order.setStrategy(strategy);

依然有switch,但至少集中在一处。进一步优化:使用注册表 + 反射消除switch

4.2 使用Map注册策略(推荐)

public class DiscountStrategyFactory {
    private static final Map<String, DiscountStrategy> strategies = new HashMap<>();
    
    static {
        strategies.put("VIP", new VipDiscountStrategy());
        strategies.put("GOLD", new GoldDiscountStrategy());
        strategies.put("SILVER", new SilverDiscountStrategy());
        strategies.put("REGULAR", new RegularDiscountStrategy());
    }
    
    public static DiscountStrategy getStrategy(String userLevel) {
        return strategies.getOrDefault(userLevel, strategies.get("REGULAR"));
    }
}

这样,新增策略只需在Map中添加一行,无需修改工厂逻辑。完美符合开闭原则

五、策略模式+工厂模式 = Java面向对象的极致体现

组合之后,架构清晰:

为什么说这是面向对象的体现?

  1. 封装:每个策略算法独立封装,互不影响。

  2. 继承/多态:策略接口统一,具体策略通过多态实现。

  3. 开闭原则:新增策略无需修改原有代码,只需添加新类+注册。

  4. 单一职责:每个策略类只负责一种算法;工厂只负责创建;上下文只负责委托。

六、实战:替换RAG系统中的if-else

在你的RAG项目中,可能需要对不同文档类型(PDF、Word、Markdown)采用不同的切分策略。用if-else

if ("pdf".equals(type)) {
    return parsePdf(file);
} else if ("docx".equals(type)) {
    return parseDocx(file);
} else if ("md".equals(type)) {
    return parseMd(file);
}

改用策略+工厂

// 策略接口
public interface ParseStrategy {
    List<String> parse(File file);
}

// 具体策略
public class PdfParseStrategy implements ParseStrategy { ... }
public class DocxParseStrategy implements ParseStrategy { ... }

// 工厂
public class ParseStrategyFactory {
    private static final Map<String, ParseStrategy> map = new HashMap<>();
    static {
        map.put("pdf", new PdfParseStrategy());
        map.put("docx", new DocxParseStrategy());
        map.put("md", new MdParseStrategy());
    }
    public static ParseStrategy getStrategy(String type) {
        return map.get(type);
    }
}

// 使用
ParseStrategy strategy = ParseStrategyFactory.getStrategy(fileType);
List<String> chunks = strategy.parse(file);

从此,新增一种文档格式只需写新的策略类并在工厂注册一行。原有代码纹丝不动。

七、总结

  • 策略模式:将算法封装成独立类,消除条件分支。

  • 工厂模式:负责创建策略对象,解耦客户端与具体策略。

  • 组合使用:既消灭了if-else,又实现了开闭原则,是Java面向对象编程的经典实践。

下次当你看到超过3个if-else分支时,就该考虑重构为策略模式了。写出的代码将更易读、易测、易扩展。

        你的项目中还有哪些地方被if-elseswitch支配着?试着用策略模式重构一下,看看代码减少了多少行?或者你遇到过策略类过多导致工厂膨胀的问题吗?欢迎分享你的重构经验。

更多推荐