
先说结论这两个模式在类图上长得确实很像甚至有人说“把工厂里的if-else搬到Context里就成了策略”。这种说法听着像那么回事但真在项目里用起来就会发现完全不是一回事。简单工厂解决的是“对象怎么创建”的问题策略模式解决的是“行为怎么替换”的问题。一个管“生”一个管“用”从意图上就是两条路线。我刚工作那会儿也踩过这个坑。接到一个需求要根据不同渠道走不同的下单逻辑我照着网上的例子写了个策略模式结果代码评审被问了一句“你的策略对象是哪里来的”我愣了一下——策略类的实例化被我写在Controller里了一堆if-else又换了个地方冒出来。后来才想明白这两个模式在真实项目里经常是一起出现的工厂负责把策略对象“造”出来策略模式负责把算法“用”起来。搞懂它们各自的边界比背定义重要得多。1. 先搞清楚各自在解决什么问题1.1 简单工厂把“创建”这个动作收拢起来简单工厂的核心痛点很朴素你的业务代码里到处是new XXX()一旦构造逻辑发生变化——比如构造函数加了参数、换了个实现类——所有调用点都要跟着改。简单工厂做的事情就是把这些new收拢到一个地方由工厂类根据入参决定返回哪个实现。它本质上是一种“创建型”的兜底方案。注意它不是23种经典设计模式之一更多是一种编程习惯或者约定但在实际项目里出现频率比很多正统模式都高。用生活里的例子理解你去便利店买饮料不需要知道饮料是哪个厂产的、怎么灌装的你只要告诉店员“来瓶可乐”店员从货架拿给你就行。这个“店员”就是工厂你不需要关心货架背后的事情。代码结构上极其直白public interface Payment { void pay(double amount); } public class Alipay implements Payment { Override public void pay(double amount) { System.out.println(支付宝支付: amount); } } public class WechatPay implements Payment { Override public void pay(double amount) { System.out.println(微信支付: amount); } } public class PaymentFactory { public static Payment create(String channel) { if (alipay.equals(channel)) { return new Alipay(); } else if (wechat.equals(channel)) { return new WechatPay(); } throw new IllegalArgumentException(未知支付渠道: channel); } }调用的地方非常干净Payment payment PaymentFactory.create(alipay); payment.pay(100);好处是调用方和具体类彻底解耦了坏处也明显工厂里的if-else会越来越长每次加一种支付方式就要改工厂。这个问题后面再说怎么缓解。1.2 策略模式把“算法”封装成可替换的组件策略模式解决的是另一个问题一个业务动作有多种实现方式而且这些方式在运行时可能要切换。它的意图是“定义一族算法分别封装起来让它们可以互相替换”替换之后不影响使用方。还是用例子的方式说。假设你在做一个促销系统满减、折扣、立减是三种不同的计价规则。如果用if-else写public double calc(double amount, String type) { if (fullReduction.equals(type)) { // 满200减30 } else if (discount.equals(type)) { // 打八折 } else if (directReduction.equals(type)) { // 立减10 } }这种写法的问题不在于“代码多”——而在于每次新增一种促销规则你都得改这个方法而且这个方法会越来越长。更麻烦的是不同规则的内部计算逻辑都堆在一起没法单独测试。策略模式的写法是把这个方法拆散成独立的类public interface DiscountStrategy { double calc(double amount); } public class FullReductionStrategy implements DiscountStrategy { Override public double calc(double amount) { return amount 200 ? amount - 30 : amount; } } public class DiscountStrategyImpl implements DiscountStrategy { private double rate; // 构造函数、getter/setter省略 Override public double calc(double amount) { return amount * rate; } } public class DirectReductionStrategy implements DiscountStrategy { private double reduction; Override public double calc(double amount) { return amount - reduction; } }关键在上下文Context这个角色上。策略模式需要一个地方持有策略引用并调用它public class PromotionContext { private DiscountStrategy strategy; public void setStrategy(DiscountStrategy strategy) { this.strategy strategy; } public double execute(double amount) { return strategy.calc(amount); } }使用方可以随时切换策略PromotionContext context new PromotionContext(); context.setStrategy(new FullReductionStrategy()); double price1 context.execute(300); context.setStrategy(new DiscountStrategyImpl(0.8)); double price2 context.execute(300);这才是策略模式的核心算法的定义和使用分离而且算法可以动态替换。2. 同一个场景两套打法的实际代码差异为了看得更清楚我用同一个“支付”场景分别写两种实现放在一起对比这样差异就非常直观了。2.1 场景描述电商系统里用户在下单时要选择支付方式支付宝、微信、银行卡。这三种支付方式的处理逻辑不同——调用的第三方接口不同、验签方式不同、回调处理不同。现在需要设计这一段代码。2.2 用简单工厂实现工厂模式的做法是把“创建支付对象”的逻辑收拢在工厂里使用时创建一个具体对象后续直接调用。public class PayService { public void pay(String channel, double amount) { Payment payment PaymentFactory.create(channel); payment.pay(amount); } }这个设计下客户端只知道Payment接口和工厂的create方法不知道也不关心具体是哪个实现类在处理。但注意支付方式的选择在调用之前就已经确定了——create(alipay)造出一个支付宝对象后面再想切到微信你必须重新调一次工厂。2.3 用策略模式实现策略模式的做法是把不同的支付方式作为可替换的算法族上下文Context持有策略引用可以在运行中切换。public class PaymentContext { private PaymentStrategy strategy; public PaymentContext(PaymentStrategy strategy) { this.strategy strategy; } public void setStrategy(PaymentStrategy strategy) { this.strategy strategy; } public void executePay(double amount) { strategy.pay(amount); } } // 使用 PaymentContext context new PaymentContext(new AlipayStrategy()); context.executePay(100); context.setStrategy(new WechatStrategy()); context.executePay(200);对比一下就会发现策略模式多了一个“上下文持有与切换”的动作它允许你在一次业务处理过程中动态改变行为。而简单工厂只负责“给你一个东西”至于这个“东西”后续怎么用工厂不管。2.4 本质区别一一个是“找东西”一个是“换行为”简单工厂的思路是——我给你一个创建设备你告诉我要什么我给你造出什么。它关心的是创建这件事。调用方拿到的对象从头到尾是同一个不会中途变化。策略模式的核心是——我给你一个容器你把算法放进去容器帮你调用。它关心的是行为替换这件事。容器的持有者可以在运行过程中换掉算法换完之后后续行为立即改变。这种差异直接决定了它们在代码结构上的不同简单工厂的“工厂类”是使用方主动调用的工厂不持有状态策略模式的“上下文类”是使用方持有引用的上下文本身维护了当前策略的状态。3. 从五个维度深挖两个模式的差异前面的例子已经能看出大致区别了但面试或者实际设计时只到这一步还不够。我从五个更深的维度拆一下。3.1 设计意图创建型 vs 行为型这是最根本的区别。设计模式经典分类里简单工厂属于创建型策略模式属于行为型。简单工厂关注“怎么创建”把对象的实例化过程从客户端代码中抽离屏蔽构造细节。策略模式关注“怎么使用”把算法的实现细节封装成独立的策略类屏蔽行为的变化。在UML类图上两者都有一个公共接口和一组实现类这个结构高度相似所以光看类图确实不好区分。但看意图就非常清楚因为“用户要一种支付方式”而去创建对象是工厂因为“计价规则可以替换”而去封装算法是策略。我自己的经验是当项目里有人在讨论“这个对象应该由谁来创建”的时候多半是工厂的用武之地当项目里有人在讨论“这段逻辑以后会怎么变、怎么扩展”的时候多半该考虑策略。3.2 变更入口改工厂 vs 改调用方简单工厂的扩展点在工厂类的if-else分支上。每增加一种产品就要打开工厂加一个分支这违反开闭原则但带来的好处是——客户端代码完全不用动。你的Controller、Service层一行不改新功能就上线了。策略模式的扩展点是新增一个策略实现类。新增策略时客户端代码有时候也不需要大改但上下文对象的管理方式会变——你要把新的策略对象传进去。如果你选择策略的地方还是if-else那么变化会转移到“选择策略”的代码中这就是为什么很多人说“策略模式把if-else搬到别处了”的原因。3.3 决策时机启动时定生死 vs 运行时随意切换简单工厂的选择发生在“创建对象的那一刻”。一旦创建完成这个对象就是某个具体类型你没法让它“变成”另一个类型。这就像一个一次性选路选定了就不回头。策略模式的选择发生在“调用行为的每一刻”。上下文持有的引用是字段可以随时set。如果经过一次判断选定了策略A运行中发现条件变了还可以切换到策略B。这种“运行时可变”的能力是策略模式独有的。实际项目里的典型用例就是多级促销用户加购时先按折扣策略算下单时如果满足满减门槛系统在同一个业务上下文里切换成满减策略整个过程不需要重新创建业务对象。3.4 扩展方式加类 vs 加类加配置表面上两者都是加新类但复杂度不同。简单工厂新增产品时新增一个实现类 在工厂加一个分支。分支越来越多工厂类会膨胀。更复杂的情况是不同产品有不同的构造参数工厂的create方法签名会变得越来越难维护。策略模式新增算法时新增一个策略实现类 在使用时传入不同策略实例。因为策略模式本身不负责创建策略对象对象的创建可以交给工厂、IoC容器甚至反射来做。这就意味着策略模式天然更适合和依赖注入结合。3.5 组合形态工厂生产策略策略消费算法这是最容易被忽略的一点。这两个模式不是互斥的反而是天然的搭档。如果你的策略对象需要根据配置创建、需要处理不同的构造参数那么简单工厂正好可以作为策略对象的“生产者”。我在实际项目里的做法是策略接口定义行为策略工厂根据配置创建具体策略上下文持有当前策略并执行。这样三层分工非常清晰// 策略工厂 public class DiscountStrategyFactory { public static DiscountStrategy create(String type, JSONObject config) { switch (type) { case fullReduction: return new FullReductionStrategy(config.getDouble(threshold), config.getDouble(reduction)); case discount: return new DiscountStrategyImpl(config.getDouble(rate)); case directReduction: return new DirectReductionStrategy(config.getDouble(reduction)); default: throw new IllegalArgumentException(Unknown type: type); } } } // 使用工厂创建策略上下文执行策略 DiscountStrategy strategy DiscountStrategyFactory.create(discount, config); PromotionContext context new PromotionContext(strategy); double price context.execute(300);这样设计之后工厂负责“创建”策略负责“执行”各管一段互不干扰。你甚至可以进一步用Spring的Bean机制替代工厂的if-else通过beanName直接获取策略实例工厂分支的问题也就自然消失了。4. 典型误区和实战避坑记录踩过不少坑加上看了很多同事写的代码我把常见的误区和经验整理成几条每一条背后都有真实场景。4.1 误区一只要有接口加多个实现就是策略模式这是最普遍的理解偏差。新人写代码时遇到“一个接口、多个实现类、一个选择器方法”就说是策略模式。实际上如果那个“选择器方法”里用if-else判断类型后直接返回实现那是简单工厂——你把创建和选择混在一起了。判断标准就一条选择器是返回新对象还是持有对象并调用方法。前者是工厂逻辑后者才是策略上下文。如果一个类同时做了这两件事——既能创建策略又能执行策略——那它不是纯粹的任何一个模式但实践中偶尔也能接受只是扩展时会难受。4.2 误区二把策略对象创建逻辑塞进上下文有次看到同事写的代码上下文类构造方法里接收一个字符串类型的type然后内部写了三行if-else来创建策略。我当时就说这不是策略模式这是把工厂的责任混到上下文里了。真正的策略模式要求上下文只依赖策略接口不应该知道具体策略类的存在。一旦上下文内部出现了new AlipayStrategy()这样的代码模式的意义已经被破坏了。如果确实需要在上下文里自给自足地创建策略那应该把创建逻辑抽成工厂再通过工厂对象注入上下文保持上下文的纯净。4.3 误区三所有if-else都应该换成策略这个属于反向的过度设计。真实项目里大部分if-else分支只出现一两次未来也不会有更多分支强行上策略模式只会增加类和调用链阅读成本远大于收益。我自己的判断标准分支数超过三个、且未来有明显扩展趋势、或者不同分支内部逻辑差异较大——这三个条件同时满足才考虑策略模式。否则用简单的if-else或者Map映射就够了。设计模式是解决问题的工具不是炫技的模板。4.4 避坑技巧策略类的无状态设计策略对象通常是多线程共享的。比如Spring容器里的Bean默认是单例。如果你的策略类里有可变的成员变量在多线程环境下就会出现数据错乱。所以策略类最好设计成无状态的——所有计算参数通过方法入参传递而不是存在字段里。万一确实需要带配置比如折扣率就把配置作为构造参数传入并在类内部用final字段保存保证对象一旦创建就不可变。这样既保留了策略的差异性又避免了线程安全问题。4.5 避坑技巧用一个Map替代工厂的if-else当策略种类多到工厂代码膨胀时可以用Map来维护“类型 - 创建函数”的映射把分支判断改成查表。以支付工厂为例public class PaymentFactory { private static final MapString, SupplierPayment CREATORS new HashMap(); static { CREATORS.put(alipay, Alipay::new); CREATORS.put(wechat, WechatPay::new); } public static Payment create(String channel) { SupplierPayment creator CREATORS.get(channel); if (creator null) { throw new IllegalArgumentException(未知渠道: channel); } return creator.get(); } }这样新增支付方式时只需要注册一个新的Supplier甚至可以把注册动作分散到各个实现类的静态代码块里工厂本身就不用改了。4.6 避坑技巧策略选择逻辑要和业务规则剥离有时候策略的选择不是简单地一个type对应一个策略而是要结合业务上下文比如订单金额、用户等级、库存状态综合判断。这种选择逻辑如果散落在各个业务调用点就会重复且容易漏。更好的做法是把“选择策略”这件事也封装起来比如做一个StrategySelector传入业务参数返回策略实例。public interface DiscountStrategySelector { DiscountStrategy select(Order order); }实现类里可以写复杂的规则甚至可以把规则配成JSON做成可动态调整的策略路由。这是策略模式加上一点点规则引擎的思想应付业务规则频繁变化的场景非常有效。5. 一张表总结以及选型建议最后用一张表把核心对比项收拢起来方便收藏和查阅。维度简单工厂策略模式模式分类创建型行为型核心意图封装对象创建逻辑封装算法并支持替换决策时机对象创建时确定行为执行时可持续切换关键角色工厂类上下文Context是否持有状态工厂一般不持有状态上下文持有当前策略引用扩展方式加产品 改工厂分支加策略实现类 组装新策略典型场景根据参数创建不同类型的对象多种算法、规则动态切换常见误用工厂if-else膨胀上下文混入创建逻辑与对方关系可以为策略模式创建策略对象需要工厂或容器来装配选型的时候很简单你现在缺的是“一个东西”就上简单工厂缺的是“一种行为的变化”就上策略模式。如果你既需要按条件创建策略对象又需要在运行中切换行为——那就两个都用工厂负责生策略负责用。我在实际项目中体会最深的是设计模式的学习不能停留在类图的相似度上要多问一句“这个模式到底把什么变化封装起来了”。简单工厂封装的是“创建方式的变化”策略模式封装的是“行为算法的变化”。一旦你想明白了自己要封装的变量是什么选哪个模式几乎不用犹豫。写代码的时候不妨把“变化点”先列出来再决定怎么设计十年后回头看那些一开始丑陋if-else的项目往往都是因为当时没想清楚这个问题。