策略模式如何替代if-else:从“面条代码”到Java面向对象的优雅转身
写在前面
写业务代码时,最常见的“坏味道”就是满屏的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; // 普通无折扣 } }痛点:
每增加一种用户等级,就要修改这个函数,违反开闭原则。
折扣算法可能在其他地方复用,只能复制粘贴。
单元测试需要覆盖所有分支,复杂且易遗漏。
多个
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面向对象的极致体现
组合之后,架构清晰:
为什么说这是面向对象的体现?
封装:每个策略算法独立封装,互不影响。
继承/多态:策略接口统一,具体策略通过多态实现。
开闭原则:新增策略无需修改原有代码,只需添加新类+注册。
单一职责:每个策略类只负责一种算法;工厂只负责创建;上下文只负责委托。
六、实战:替换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-else或switch支配着?试着用策略模式重构一下,看看代码减少了多少行?或者你遇到过策略类过多导致工厂膨胀的问题吗?欢迎分享你的重构经验。
