资讯动态

Java策略模式实战:告别if-else构建可扩展业务逻辑

发布时间:2026/10/9 12:32:44 来源:尧图企业网站定制
讲Java后端开发里的可扩展业务逻辑设计绕不开的一个经典套路就是策略模式。我最早真正意识到它的价值是在一个订单项目里支付方式、配送规则、营销折扣全堆在一个service方法里几十个if-else嵌套每次产品提新需求我都要在一大坨代码里找到对应的分支测一遍还要担心改坏其它逻辑。后来用策略模式重构之后加一种支付渠道只需要新增一个类主流程一行不动那种感觉确实很不一样。这篇文章会把策略模式从概念到落地完整过一遍适合刚接触设计模式、或者项目里if-else已经多到看不下去的Java工程师参考。我会从最原始的写法开始逐步演化到Spring Boot项目里的实际应用再把我踩过的坑和排查心得一并写出来。内容会尽量贴合真实业务代码可以直接拿到项目里改造。1. 先搞清楚策略模式到底在解决什么问题1.1 没有策略模式的业务代码长什么样我先给你看一段很常见的烂代码。假设现在要做一个支付功能支持支付宝、微信、银行卡三种渠道很多新手会这么写public class PaymentService { public void pay(String channel, BigDecimal amount) { if (alipay.equals(channel)) { System.out.println(调用支付宝接口金额 amount); // 支付宝支付逻辑... } else if (wechat.equals(channel)) { System.out.println(调用微信支付接口金额 amount); // 微信支付逻辑... } else if (creditCard.equals(channel)) { System.out.println(调用银行卡接口金额 amount); // 银行卡支付逻辑... } else { throw new IllegalArgumentException(不支持的支付渠道 channel); } } }这段代码在只支持两三种渠道的时候问题还不算严重。等到支付渠道增加到七八种各种渠道又有不同的参数校验、不同的回调处理这个pay方法就会膨胀到几百行。每次新增渠道你都要打开这个核心方法找到合适的位置塞一段新逻辑。改一处就得回归测试所有老渠道因为谁也不敢保证改这段代码会不会影响到其它分支。这种写法的核心问题在于实现和选择逻辑强耦合。支付的具体行为和根据渠道选择行为的判断搅在一起违背了面向对象设计里的单一职责原则。代码越改越乱测试也越来越难写。1.2 策略模式的核心定义与角色拆解策略模式的官方定义是定义一组算法将每个算法都封装起来并且使它们之间可以相互替换。这句话听着抽象我换个方式解释。你先想象一个场景你要从家里去机场可以打车、坐地铁、骑共享单车。这三种交通方式就是三个策略它们解决的问题一模一样——到达机场但具体实现完全不一样。作为出行的人你不用管车怎么开、地铁怎么换乘只需要告诉导航我要去机场导航会根据实时路况帮你选一种方式。在这个类比里策略接口就是到达机场这个抽象目标具体策略就是打车、地铁、骑行这些具体出行方案上下文就是导航它持有某个具体策略并在需要时调用它。对应到Java代码里策略模式通常有三个角色角色说明例子策略接口定义一组可执行的动作是所有策略的公共抽象PaymentStrategy接口的pay()方法具体策略类实现策略接口每个类封装一种具体算法或业务规则AlipayStrategy、WechatPayStrategy上下文类持有策略接口的引用负责调用策略屏蔽上层对具体策略的依赖PaymentContext你发现了没有核心就是面向接口编程。上层代码只依赖PaymentStrategy这个抽象不碰任何具体实现类新增策略的时候上层代码完全不用变这就是开闭原则——对扩展开放对修改关闭。2. 从零开始手写一个策略模式2.1 定义策略接口先定义一个策略接口。这里我建议把方法的语义定义得足够清晰参数和返回值都要认真想好因为这个接口一旦定下来后面所有策略类都要遵循它。就拿支付来说可以定义成下面这样public interface PaymentStrategy { /** * 执行支付 * * param orderNo 订单号 * param amount 支付金额 * return 支付结果 */ PayResult pay(String orderNo, BigDecimal amount); }注意接口的定义不能太窄。比如你只定义了pay(String orderNo, BigDecimal amount)后续某个渠道需要传优惠券ID这个接口就传不了只能改接口签名所有策略类都得跟着改。所以在真实项目里我会倾向于把参数包装成一个请求对象public class PayRequest { private String orderNo; private BigDecimal amount; private Long couponId; private Long userId; // getter / setter 省略... }这样接口签名就稳定多了public interface PaymentStrategy { PayResult pay(PayRequest request); }这算是我踩过坑之后总结出来的经验策略接口的参数要设计得足够通用最好用对象封装而不是用一堆散列的参数。接口一旦被多个实现类依赖改签名成本很高。2.2 实现具体策略类有了接口就可以写具体策略了。每种支付渠道一个类各自封装自己的逻辑public class AlipayStrategy implements PaymentStrategy { Override public PayResult pay(PayRequest request) { System.out.println(支付宝支付订单号 request.getOrderNo() 金额 request.getAmount()); // 此处调用支付宝SDK... return PayResult.success(); } } public class WechatPayStrategy implements PaymentStrategy { Override public PayResult pay(PayRequest request) { System.out.println(微信支付订单号 request.getOrderNo() 金额 request.getAmount()); // 此处调用微信支付SDK... return PayResult.success(); } } public class CreditCardStrategy implements PaymentStrategy { Override public PayResult pay(PayRequest request) { System.out.println(银行卡支付订单号 request.getOrderNo() 金额 request.getAmount()); // 此处调用银联SDK... return PayResult.success(); } }每个策略类只关心自己的实现细节互不干扰。支付宝的策略类里哪怕写了五百行SDK调用逻辑也不会影响微信策略类。这就是封装变化的好处——把每个渠道的差异隔离在各自的类里而不是堆在同一个方法里相互纠缠。2.3 搭建策略上下文策略接口和实现类都有了但上层业务方怎么用呢有两种风格一种是写一个上下文类把策略接口组合进来另一种是直接维护一个策略Map通过key获取。我先说最经典的上下文写法。public class PaymentContext { private PaymentStrategy strategy; public PaymentContext(PaymentStrategy strategy) { this.strategy strategy; } public PayResult execute(PayRequest request) { return strategy.pay(request); } }上下文类的核心作用是把持有策略和调用策略的动作封装起来。调用方不需要关心当前底层到底跑的是哪个策略只要跟PaymentContext打交道就行。按照一般的设计套路这个上下文还可以配合简单工厂使用根据传入的渠道类型在上下文内部创建对应的策略对象。这样上层连策略对象都不需要接触只传一个渠道标识和请求参数public class PaymentContext { public PayResult pay(String channel, PayRequest request) { PaymentStrategy strategy createStrategy(channel); return strategy.pay(request); } private PaymentStrategy createStrategy(String channel) { if (alipay.equals(channel)) { return new AlipayStrategy(); } if (wechat.equals(channel)) { return new WechatPayStrategy(); } if (creditCard.equals(channel)) { return new CreditCardStrategy(); } throw new IllegalArgumentException(不支持的支付渠道 channel); } }不过这种写法只是把if-else挪了个位置并没有完全消灭判断逻辑。在纯Java工程里还能接受但到了Spring Boot项目里我们有更优雅的解法后面会详细讲。2.4 客户端如何调用先看一眼手写版本在调用方怎么用public class OrderService { private PaymentContext paymentContext; public void checkout(String channel, Order order) { PayRequest request new PayRequest(); request.setOrderNo(order.getOrderNo()); request.setAmount(order.getAmount()); paymentContext.pay(channel, request); } }到这里策略模式的雏形就出来了。对比一下最开始的if-else写法新增一个支付渠道的时候原来的代码需要打开PaymentService的pay方法改逻辑而现在只需要新增一个策略类调用方和上下文完全不用动。注意这里说的不用动是指主流程不用动。但如果你用的是createStrategy里的if-else那还得去工厂里加一个分支。所以纯手写策略模式的意义更多在于隔离变化真正的不改代码还得靠Spring容器帮助我们管理策略对象。3. 在Spring Boot项目中落地策略模式3.1 把策略类交给Spring管理纯手写的策略模式在实际项目里会遇到一个问题策略类的创建和生命周期管理很麻烦。如果每种策略都涉及数据库操作、Redis调用、第三方SDK手动new出来的策略对象根本拿不到那些依赖。所以到了Spring Boot项目里第一步就是把策略类交给Spring容器管理Component public class AlipayStrategy implements PaymentStrategy { Autowired private AlipayClient alipayClient; // 注入SDK客户端 Override public PayResult pay(PayRequest request) { // 使用注入的 alipayClient 完成支付... } } Component public class WechatPayStrategy implements PaymentStrategy { Autowired private WechatPayClient wechatPayClient; Override public PayResult pay(PayRequest request) { // 使用注入的 wechatPayClient 完成支付... } }每个策略类都加上Component注解让Spring扫描到它们这些策略Bean就能自动获得自己需要的各种依赖。它们默认是单例的整个应用生命周期只有一个实例这对无状态的策略类来说也很合适。3.2 用Map自动注入替换手工工厂这是整个落地过程中我最推荐的一个技巧。Spring允许把同一接口的所有实现类自动收集到一个Map中key是Bean名称value是策略对象Service public class PaymentService { private final MapString, PaymentStrategy strategyMap; public PaymentService(MapString, PaymentStrategy strategyMap) { this.strategyMap strategyMap; } public PayResult pay(String channel, PayRequest request) { PaymentStrategy strategy strategyMap.get(channel); if (strategy null) { throw new IllegalArgumentException(不支持的支付渠道 channel); } return strategy.pay(request); } }这里MapString, PaymentStrategy的key默认是Bean名称也就是策略类的类名首字母小写alipayStrategy、wechatPayStrategy、creditCardStrategy。如果我们约定策略标识就用这些Bean名那路由逻辑一行代码就搞定了。这个方案比手写工厂优雅得多因为新增策略类时不需要在PaymentService里改任何代码Spring容器自动管理策略的生命周期和依赖注入查询Map的复杂度是O(1)性能完全不用担心。如果不想用Bean名当key还可以在策略类上使用Component(alipay)显式指定Bean名称让代码可读性更好Component(alipay) public class AlipayStrategy implements PaymentStrategy { // ... }那么调用的时候直接strategyMap.get(alipay)就能拿到对应的策略。3.3 策略枚举统一管理策略类型在实际项目里渠道类型通常不会直接传字符串而是定义成枚举这样类型更安全。枚举可以和策略Map配合得很好public enum PaymentChannel { ALIPAY(alipay, 支付宝), WECHAT(wechat, 微信支付), CREDIT_CARD(creditCard, 银行卡支付); private final String code; private final String desc; PaymentChannel(String code, String desc) { this.code code; this.desc desc; } public String getCode() { return code; } }然后路由的时候先根据前端传的枚举值拿到code再通过code去策略Map里找public PayResult pay(PaymentChannel channel, PayRequest request) { PaymentStrategy strategy strategyMap.get(channel.getCode()); if (strategy null) { throw new IllegalArgumentException(不支持的支付渠道 channel.getCode()); } return strategy.pay(request); }这一步看着简单但对项目的可维护性影响很大。枚举把渠道类型收敛在一个地方前后端对接、文档编写、参数校验都有据可查。前端传一个xxx进来代码里一眼就能看出它对应的是哪个渠道、描述是什么。实操心得我在项目里习惯把策略标识放在枚举里统一管理而不是散落在常量类或者前端文档里。这样在新增策略时只需要同时修改枚举和新增策略类两个地方测试人员拿着枚举列表就能写用例。3.4 构造函数注入还是字段注入用MapString, PaymentStrategy注入的时候我推荐使用构造函数注入。原因很简单第一所有依赖在对象创建时就必须就位不会出现运行到一半发现strategyMap为null的情况第二单元测试的时候直接new一个PaymentService传一个Map进去就行不需要反射或者Mockito去设置私有字段第三IDE也能帮你一眼看出这个类依赖了哪些东西。Service public class PaymentService { private final MapString, PaymentStrategy strategyMap; public PaymentService(MapString, PaymentStrategy strategyMap) { this.strategyMap strategyMap; } // ... }如果你用的是Lombok还可以简化成RequiredArgsConstructorService RequiredArgsConstructor public class PaymentService { private final MapString, PaymentStrategy strategyMap; // ... }这样代码更简洁功能完全一样。4. 真实业务场景配送费计算与营销折扣的可扩展设计4.1 业务需求拆解光说不练假把式。我拿一个常见的电商场景来讲配送费计算。现在业务方给了规则普通会员满100元包邮不满收10元运费银卡会员满80元包邮不满收8元运费金卡会员满50元包邮不满收5元运费钻石会员永远包邮。这个需求最适合用策略模式因为规则按会员等级划分每种等级的计费逻辑相对独立而且后续大概率会增加新的会员等级。如果把这些规则全写在一个deliveryFee()方法里过两个月又会变成一坨if-else。4.2 策略接口与实现类设计定义一个配送费策略接口public interface DeliveryFeeStrategy { /** * 计算配送费 * * param orderAmount 订单金额 * return 配送费 */ BigDecimal calculate(BigDecimal orderAmount); }然后分别实现各个等级的策略Component(NORMAL) public class NormalMemberDeliveryFeeStrategy implements DeliveryFeeStrategy { private static final BigDecimal FREE_THRESHOLD new BigDecimal(100); private static final BigDecimal FEE new BigDecimal(10); Override public BigDecimal calculate(BigDecimal orderAmount) { if (orderAmount.compareTo(FREE_THRESHOLD) 0) { return BigDecimal.ZERO; } return FEE; } } Component(SILVER) public class SilverMemberDeliveryFeeStrategy implements DeliveryFeeStrategy { private static final BigDecimal FREE_THRESHOLD new BigDecimal(80); private static final BigDecimal FEE new BigDecimal(8); Override public BigDecimal calculate(BigDecimal orderAmount) { if (orderAmount.compareTo(FREE_THRESHOLD) 0) { return BigDecimal.ZERO; } return FEE; } } Component(GOLD) public class GoldMemberDeliveryFeeStrategy implements DeliveryFeeStrategy { private static final BigDecimal FREE_THRESHOLD new BigDecimal(50); private static final BigDecimal FEE new BigDecimal(5); Override public BigDecimal calculate(BigDecimal orderAmount) { if (orderAmount.compareTo(FREE_THRESHOLD) 0) { return BigDecimal.ZERO; } return FEE; } } Component(DIAMOND) public class DiamondMemberDeliveryFeeStrategy implements DeliveryFeeStrategy { Override public BigDecimal calculate(BigDecimal orderAmount) { return BigDecimal.ZERO; } }这里我把Bean名称直接指定成会员等级的枚举code比如NORMAL、SILVER、GOLD、DIAMOND这样路由的时候不需要再做转换拿着会员等级枚举的code直接去Map里取就行。4.3 策略选择与路由逻辑会员等级这里也用一个枚举public enum MemberLevel { NORMAL(NORMAL, 普通会员), SILVER(SILVER, 银卡会员), GOLD(GOLD, 金卡会员), DIAMOND(DIAMOND, 钻石会员); private final String code; private final String desc; // 构造方法、getter 省略... }配送费的计算入口Service RequiredArgsConstructor public class DeliveryFeeService { private final MapString, DeliveryFeeStrategy strategyMap; public BigDecimal calculate(MemberLevel memberLevel, BigDecimal orderAmount) { DeliveryFeeStrategy strategy strategyMap.get(memberLevel.getCode()); if (strategy null) { throw new IllegalArgumentException(暂不支持该会员等级的配送费计算 memberLevel.getCode()); } return strategy.calculate(orderAmount); } }后面的逻辑就非常清楚了请求进来根据会员等级拿到策略再执行业务计算。如果用户是钻石会员直接返回0如果是普通会员走满100包邮、不满收10元的规则。等产品经理提需求说要新增一个黑钻会员我需要做的事只有两步加一个MemberLevel枚举值再加一个Component(BLACK_DIAMOND)的策略实现类。至于DeliveryFeeService一个字都不用改。这就是可扩展业务逻辑设计最直观的体现。4.4 营销折扣场景怎么套策略模式营销折扣跟配送费计算是同一类问题。什么满减、折扣、新人立减、无门槛券本质上都是给定一个原始金额算出一个优惠后金额。套用策略模式先定义一个接口public interface DiscountStrategy { BigDecimal discount(BigDecimal originalAmount, OrderContext context); }每种营销玩法一个实现类再用一个Map收集起来路由规则可以根据context里的优惠券类型、用户标签等条件去选择。这里我额外说一句营销折扣往往不是单一策略生效可能会叠加多个策略这是策略模式的一个变体问题。处理叠加时可以用一个责任链式的策略列表把多个DiscountStrategy串起来依次执行而不是在策略内部去判断我的前面还有谁。5. 进阶玩法策略模式与其它模式组合5.1 用Function/Lambda简化轻量级策略有些读者可能会问如果策略实现很简单每个策略就一两行代码非要建一个接口加好几个实现类是不是太笨重了确实策略模式有一定的仪式感在规则特别简单的时候有点杀鸡用牛刀。Java 8以后可以用Function接口配合Lambda表达式来实现轻量级的策略注册。比如下面的写法Service public class DiscountService { private final MapString, FunctionBigDecimal, BigDecimal discountStrategyMap new HashMap(); PostConstruct public void init() { discountStrategyMap.put(NONE, amount - amount); discountStrategyMap.put(PERCENT_90, amount - amount.multiply(new BigDecimal(0.9))); discountStrategyMap.put(FIXED_10, amount - amount.subtract(new BigDecimal(10))); } public BigDecimal apply(String strategyCode, BigDecimal amount) { FunctionBigDecimal, BigDecimal function discountStrategyMap.get(strategyCode); if (function null) { throw new IllegalArgumentException(未知策略 strategyCode); } return function.apply(amount); } }这种写法省去了策略接口和实现类在策略逻辑比较轻薄的时候非常高效。但它也有短板Lambda表达式适合表达单函数的逻辑如果每个策略内部还依赖其它Bean或者规则比较复杂、要写很多行代码就不适合塞在Lambda里了还是老老实实建实现类更清晰。5.2 策略模式与模板方法模式的组合另一个在真实项目中很有用的组合是策略模式加模板方法模式。策略模式解决的是同一个动作的多种实现问题但它不管完成这个动作有没有固定的流程步骤。有些业务比如对接第三方物流发货流程是固定的校验订单状态扣减库存调用物流API记录发货日志。不同物流公司可能只在第3步有差异。这种情况如果硬写策略模式会让不必要的重复代码散落在各个策略类里。更好的做法是定义一个抽象模板类把固定的步骤写死在模板方法里只留差异化的部分抽象出来交给子类实现public abstract class AbstractShipmentStrategy { public final void ship(ShipOrder order) { validateOrder(order); deductStock(order); callLogisticsApi(order); saveLog(order); } protected final void validateOrder(ShipOrder order) { // 通用校验逻辑... } protected final void deductStock(ShipOrder order) { // 通用扣库存逻辑... } protected abstract void callLogisticsApi(ShipOrder order); protected final void saveLog(ShipOrder order) { // 通用记录日志逻辑... } }然后每个物流公司实现一个子类只覆盖callLogisticsApi。这样既享受了策略模式带来的可替换性又避免了重复的模板步骤。我通常会把模板类放在策略接口的实现层次中间形成策略接口 - 抽象模板 - 具体实现这样的结构。5.3 策略模式在Spring Boot MyBatis Plus项目里的常见组合如果你用的是Spring Boot MyBatis Plus这套技术栈策略模式最常见的使用场景是按不同业务类型选择不同的数据组装或落库逻辑。比如多商户商城系统里不同类型的商户平台自营、第三方入驻、跨境商户在结算时走的逻辑不同平台自营商户直接结算到平台账户第三方入驻商户扣除平台抽成后结算跨境商户还需要做汇率换算和报关单据处理。这时候策略模式可以把结算处理器抽象出来每个商户类型一个策略类Map key用商户类型枚举。在MyBatis Plus层每个策略类可以注入自己的ServiceImpl或者Mapper专心地组装自己的SQL条件和数据。新接入一种商户类型时新增策略类和对应的Mapper方法即可原结算入口不用动。6. 常见问题与排查技巧实录6.1 策略Bean没有被Spring扫描到这是我遇到过也比较多人问的问题。明明写了Component注解但注入MapString, PaymentStrategy的时候发现少了一个策略或者运行时strategyMap.get(xxx)返回null。先检查ComponentScan的扫描范围。Spring Boot的默认扫描包是启动类所在的包及其子包如果你的策略类放在了com.example.payment.strategy启动类却在com.example那没问题但如果启动类在com.example.order就会扫不到。解决办法是调整包结构或者在启动类上用ComponentScan显式声明要扫描的包。另一个可能是策略类的命名冲突。如果两个策略类在Component里指定了相同的Bean名称Spring启动时甚至会直接报错或者后注册的Bean覆盖先注册的。排查方式是在启动日志里搜一下strategyMap或者相关的Bean名看看实际注册了几个。6.2 注入Map引发循环依赖使用构造函数注入MapString, XxxStrategy时如果某个策略类又反向依赖了注入它的Service就可能出现循环依赖。比如PaymentService要注入所有PaymentStrategy而某个AlipayStrategy内部又注入了PaymentService去查订单状态这就构成环了。解决办法有几个一是重新审视设计把策略类对Service的依赖抽出去改成通过参数传入二是用Lazy注解打破循环三是把策略Map的注入方式改成ApplicationContext获取。我最推荐的是第一种因为循环依赖本身就是设计上的味道。如果实在要快速解决给注入点加Lazy是见效最快的Service RequiredArgsConstructor public class PaymentService { private final MapString, PaymentStrategy strategyMap; }然后在一个策略类里Component public class AlipayStrategy implements PaymentStrategy { private final PaymentService paymentService; public AlipayStrategy(Lazy PaymentService paymentService) { this.paymentService paymentService; } }注意Spring Boot 2.6版本以后默认禁止了循环依赖如果你还在用旧写法构造了环升级之后启动就会报错。这也逼着你把设计理顺。6.3 Map的key到底是什么拿不到值怎么办初学者最容易踩的坑是以为MapString, PaymentStrategy的key是类名结果用的是首字母小写类名。比如类叫AlipayStrategykey其实是alipayStrategy而不是AlipayStrategy。如果代码里用AlipayStrategy去get必然返回null。解决方案就是显式指定Bean名称比如在Component(alipay)里写清楚。这看起来是个很小的细节但说实话我在排查别人代码时见过太多次因为这种大小写、命名不统一而导致的空指针。6.4 策略类的状态管理最后提醒一个设计层面的问题。Spring容器默认把策略类作为单例管理也就是说整个应用共享一个实例。策略类里面不应该保存可变的业务状态比如某个订单号、某个用户ID。如果你在策略类里写了一个私有字段去存当前处理的订单在并发请求下两个线程会互相覆盖这个字段导致数据错乱。正确的做法是所有业务参数都通过方法参数比如PayRequest传进来策略类保持无状态。这跟函数式编程里的纯函数思想一致——不依赖外部状态也不修改外部状态只根据入参计算并返回结果。6.5 常见问题速查表问题现象可能原因处理建议注入的Map里少策略扫描包没覆盖到策略类检查ComponentScan范围调整包结构两个策略类冲突Bean名称重复显式指定不同的Component(xxx)名称启动报循环依赖策略类反向依赖调用方调整依赖方向优先用Lazy临时解决反复出现空指针Bean name大小写或拼写错误统一用枚举code显式指定Bean名称并发时数据混乱策略类里有可变字段消除状态字段全部通过方法参数传值新增策略后没生效IDE缓存或没重新编译重新编译并确认目标类被Spring扫描到7. 什么时候不该用策略模式策略模式不是万能的我在项目里也见过滥用策略模式的情况。如果业务只有两种固定分支而且几十年都不会变你硬要抽出接口、写实现类、搞Map注入反而增加了代码的阅读成本。我的判断标准很简单至少有三个策略且新增策略的概率比较高才值得用策略模式。如果只有两个分支或者策略实现之间差异极大、根本没有公共抽象那硬套策略模式会非常别扭。策略接口的抽象一定要找对变与不变的边界。边界找错了策略模式不但不能帮你降复杂度反而会把一些本来简单的判断逻辑弄得支离破碎。另外如果策略选择逻辑本身超级复杂比如要考虑十几个条件才能决定用哪个策略那时策略模式只解决了执行的部分选择的部分还得单独设计一个路由组件否则路由逻辑本身也会变成一坨复杂代码。这种情况下可以考虑把策略选择也抽象出来封装成独立的StrategyRouter让路由和执行业务各司其职。回到开头那段if-else支付代码现在你再看它会发现策略模式其实不是一种高深莫测的技巧它就是面向对象多态的一个典型应用。用接口定义行为用多个实现封装差异用容器管理生命周期用约定好的标识快速路由——这套组合拳打好了业务逻辑再多也能保持清爽。我在实际项目里的体会是策略模式最有价值的地方不在于省了几行代码而在于它逼着你把业务分类沉淀下来。每一种策略就是一个业务变体枚举就是这些变体的清单。产品来问现在支持哪些渠道看枚举就知道了测试来问改动影响什么范围新加策略类不影响老逻辑回答起来也非常轻松。如果你的项目里也在被爆发式增长的if-else困扰不妨从今天这段配送费计算的例子开始找个小模块先重构试试。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑