本文 5000 字深度原创含完整代码示例和生产级落地方案。创作不易如果对你有帮助请点赞 收藏 ⭐ 关注 三连支持你的认可是我持续输出的最大动力本文是「设计模式实战解读」系列第五篇。系列文章统一按照定义 → 痛点场景 → 模式结构 → 核心实现 → 真实应用 → 常见变种 → 优缺点 → 避坑指南 → FAQ的结构展开每篇聚焦一个模式讲透。一句话定义策略模式Strategy定义一族算法把每个算法封装成独立的类使它们可以互相替换。调用方选择使用哪种策略不关心策略的内部实现。归属行为型模式。一、没有策略模式时的痛点假设你在做一个支付模块需要支持多种支付方式publicclassPaymentService{publicPayResultpay(PayRequestrequest){if(wechat.equals(request.getPayType())){// 微信支付逻辑20 行StringprepayIdwechatClient.createOrder(...);returnbuildWechatResult(prepayId);}elseif(alipay.equals(request.getPayType())){// 支付宝逻辑25 行StringtradeNoalipayClient.tradeCreate(...);returnbuildAlipayResult(tradeNo);}elseif(unionpay.equals(request.getPayType())){// 银联逻辑30 行StringtnunionPayClient.frontTransReq(...);returnbuildUnionPayResult(tn);}elseif(balance.equals(request.getPayType())){// 余额支付逻辑15 行deductBalance(request.getUserId(),request.getAmount());returnbuildBalanceResult();}// 随着支付方式增加这个方法会一直膨胀...thrownewBizException(Unsupported pay type);}}这段代码的问题违反开闭原则每增加一种支付方式都要修改这个方法方法越来越长4 种支付方式已经 90 行10 种就是 200 行难以测试测试某种支付方式要绕过所有其他分支无法复用微信支付的逻辑散在 if 块里其他地方想复用也难以抽取维护风险高修改微信支付逻辑时可能误改 if-else 结构影响其他支付方式这就是典型的分支爆炸——当业务逻辑按照某个**维度支付类型、折扣类型、导出格式…**有多种差异化实现时if-else 就会失控。二、模式结构┌──────────────────────────────┐ │ Context上下文 │ ├──────────────────────────────┤ │ - strategy: PayStrategy │ ← 持有策略引用 ├──────────────────────────────┤ │ setStrategy(PayStrategy) │ │ executeStrategy(request) │ ← 委托给策略执行 └──────────────────────────────┘ │ 依赖 ↓ ┌──────────────────────────────┐ │ PayStrategy接口 │ ├──────────────────────────────┤ │ pay(PayRequest): PayResult │ ← 统一的策略接口 └──────────────────────────────┘ ↑ ↑ ↑ WechatPay Alipay UnionPay ← 具体策略实现三个角色Strategy策略接口定义算法的统一签名ConcreteStrategy具体策略每种算法的具体实现Context上下文持有策略引用对外提供调用入口关键点Context 不知道具体用了哪个策略策略对 Context 透明。三、核心实现3.1 基础版接口 实现类// 策略接口publicinterfacePayStrategy{PayResultpay(PayRequestrequest);// 声明自己支持哪种支付类型用于策略选择StringpayType();}// 具体策略微信支付ComponentpublicclassWechatPayStrategyimplementsPayStrategy{OverridepublicStringpayType(){returnwechat;}OverridepublicPayResultpay(PayRequestrequest){// 微信支付完整逻辑独立封装互不干扰StringprepayIdwechatClient.createOrder(request.getAmount(),request.getOutTradeNo(),request.getNotifyUrl());returnPayResult.success(prepayId);}}// 具体策略支付宝ComponentpublicclassAlipayStrategyimplementsPayStrategy{OverridepublicStringpayType(){returnalipay;}OverridepublicPayResultpay(PayRequestrequest){StringtradeNoalipayClient.tradeCreate(...);returnPayResult.success(tradeNo);}}3.2 Spring 自动注册推荐// 策略上下文自动收集所有策略实现ComponentpublicclassPayContext{// Spring 把所有 PayStrategy 实现按 Bean Name 注入privatefinalMapString,PayStrategystrategyMap;publicPayContext(ListPayStrategystrategies){this.strategyMapstrategies.stream().collect(Collectors.toMap(PayStrategy::payType,Function.identity()));}publicPayResultpay(PayRequestrequest){PayStrategystrategystrategyMap.get(request.getPayType());if(strategynull){thrownewBizException(ErrorCode.PAY_TYPE_NOT_SUPPORTED,Unsupported pay type: request.getPayType());}returnstrategy.pay(request);}}改造后的 PaymentServiceServicepublicclassPaymentService{AutowiredprivatePayContextpayContext;publicPayResultpay(PayRequestrequest){// 原来 90 行变成 1 行returnpayContext.pay(request);}}新增一种支付方式比如Apple Pay只需要ComponentpublicclassApplePayStrategyimplementsPayStrategy{OverridepublicStringpayType(){returnapplepay;}OverridepublicPayResultpay(PayRequestrequest){/* ... */}}一行代码都不需要修改。这就是策略模式的核心价值让扩展变成加而不是改。3.3 函数式策略Lambda当策略逻辑简单时可以用 Lambda 代替独立类// 策略接口本身就是函数式接口FunctionalInterfacepublicinterfaceDiscountStrategy{BigDecimalcalculate(BigDecimaloriginalPrice,intquantity);}// 用 Lambda 定义策略不需要单独建类publicclassDiscountStrategyRegistry{privatestaticfinalMapString,DiscountStrategyREGISTRYnewHashMap();static{// 九折优惠REGISTRY.put(MEMBER_DISCOUNT,(price,qty)-price.multiply(newBigDecimal(0.9)));// 满100减20REGISTRY.put(FULL_REDUCTION,(price,qty)-{if(price.compareTo(newBigDecimal(100))0){returnprice.subtract(newBigDecimal(20));}returnprice;});// 阶梯折扣REGISTRY.put(LADDER_DISCOUNT,(price,qty)-{if(qty10)returnprice.multiply(newBigDecimal(0.8));if(qty5)returnprice.multiply(newBigDecimal(0.85));returnprice;});}publicstaticBigDecimalcalculate(StringdiscountCode,BigDecimalprice,intqty){DiscountStrategystrategyREGISTRY.getOrDefault(discountCode,(p,q)-p);// 默认不打折returnstrategy.calculate(price,qty);}}四、真实应用场景4.1 框架级应用框架策略接口具体策略策略选择依据Spring SecurityAuthenticationProviderDaoAuthenticationProvider / OAuthProvider认证类型Spring MVCHandlerAdapterRequestMappingHandlerAdapter / SimpleControllerHandlerAdapterController 类型MyBatisTypeHandlerStringTypeHandler / IntegerTypeHandlerJava/JDBC 类型Java 排序Comparator各种比较器排序字段Spring RetryRetryPolicySimpleRetryPolicy / ExceptionClassifierRetryPolicy重试条件4.2 业务场景业务策略维度典型策略选择依据支付支付渠道微信/支付宝/银联/余额用户选择折扣计算优惠类型会员折扣/满减/阶梯折扣活动配置消息通知通知渠道邮件/短信/钉钉/企微用户偏好文件导出导出格式Excel/CSV/PDF/HTML用户选择风控风险等级拒绝/人工审核/放行风险分数数据同步同步模式全量/增量/差量数据量和时效4.3 iPaaS 连接器鉴权策略在 iPaaS 平台中不同连接器的鉴权方式完全不同——这是策略模式的天然场景// 鉴权策略接口publicinterfaceAuthStrategy{voidauth(HttpRequestrequest,ConnectorConfigconfig);AuthTypesupportedAuthType();}// OAuth2 鉴权策略ComponentpublicclassOAuth2AuthStrategyimplementsAuthStrategy{OverridepublicAuthTypesupportedAuthType(){returnAuthType.OAUTH2;}Overridepublicvoidauth(HttpRequestrequest,ConnectorConfigconfig){StringtokentokenCache.getOrRefresh(config.getConnectorId());request.addHeader(Authorization,Bearer token);}}// API Key 鉴权策略ComponentpublicclassApiKeyAuthStrategyimplementsAuthStrategy{OverridepublicAuthTypesupportedAuthType(){returnAuthType.API_KEY;}Overridepublicvoidauth(HttpRequestrequest,ConnectorConfigconfig){request.addHeader(config.getKeyHeader(),config.getApiKey());}}// Basic Auth 鉴权策略ComponentpublicclassBasicAuthStrategyimplementsAuthStrategy{OverridepublicAuthTypesupportedAuthType(){returnAuthType.BASIC;}Overridepublicvoidauth(HttpRequestrequest,ConnectorConfigconfig){StringcredentialsBase64.encode(config.getUsername():config.getPassword());request.addHeader(Authorization,Basic credentials);}}连接器执行器中// 自动根据连接器配置的鉴权类型选择策略authStrategyMap.get(config.getAuthType()).auth(request,config);新增一种鉴权方式比如 HMAC 签名加一个HmacAuthStrategy类即可连接器执行器不需要改动一行。五、常见变种5.1 策略 工厂当策略创建本身有复杂逻辑时需要初始化状态、依赖配置用工厂来创建策略publicclassPayStrategyFactory{publicPayStrategycreate(StringpayType,PayConfigconfig){returnswitch(payType){casewechat-newWechatPayStrategy(config.getWechatAppId(),config.getMchId());casealipay-newAlipayStrategy(config.getAlipayAppId(),config.getPrivateKey());default-thrownewBizException(Unsupported: payType);};}}适用场景策略实例是有状态的、需要按配置初始化不适合作为 Spring 单例的情况。5.2 策略链责任链的变种多个策略按顺序执行直到某个策略处理成功publicclassFallbackPayContext{privatefinalListPayStrategychain;// 按优先级排序publicPayResultpay(PayRequestrequest){for(PayStrategystrategy:chain){if(strategy.supports(request)){try{returnstrategy.pay(request);}catch(PayExceptione){log.warn(Strategy {} failed, trying next,strategy.payType(),e);}}}thrownewBizException(All strategies failed);}}5.3 枚举策略当策略种类有限且固定时用枚举简洁实现publicenumShippingStrategy{STANDARD{OverridepublicBigDecimalcalculateFee(intweight,intdistance){returnnewBigDecimal(5).add(newBigDecimal(weight).multiply(newBigDecimal(0.01)));}},EXPRESS{OverridepublicBigDecimalcalculateFee(intweight,intdistance){returnnewBigDecimal(15).add(newBigDecimal(weight).multiply(newBigDecimal(0.02)));}};publicabstractBigDecimalcalculateFee(intweight,intdistance);}// 使用BigDecimalfeeShippingStrategy.EXPRESS.calculateFee(500,100);六、优缺点优点缺点消灭 if-else逻辑清晰策略类数量增加符合开闭原则新增策略不改原代码调用方需要知道有哪些策略可选每个策略独立方便单测简单场景下引入策略模式过度设计策略可以动态切换策略之间如果有共享状态需要额外处理支持组合策略链、策略 工厂策略选择逻辑本身可能变成新的 if-else七、避坑指南坑 1策略本身有状态并发问题策略通常是 Spring 单例Component如果策略类里有成员变量存储当前请求的状态多线程并发时会乱。原则策略类必须无状态——请求相关的状态通过参数传入不存在策略类的成员变量里。// ❌ 有状态的策略并发不安全ComponentpublicclassWechatPayStrategyimplementsPayStrategy{privatePayRequestcurrentRequest;// 危险多线程会乱publicPayResultpay(PayRequestrequest){this.currentRequestrequest;// 并发问题...}}// ✓ 无状态策略正确ComponentpublicclassWechatPayStrategyimplementsPayStrategy{publicPayResultpay(PayRequestrequest){// request 是方法参数线程安全...}}坑 2策略选择逻辑本身又变成 if-else// ❌ 用 if-else 选择策略治好了一个 if-else 又来一个Stringstrategy;if(order.isMember()){strategyMEMBER_DISCOUNT;}elseif(order.getTotal().compareTo(newBigDecimal(100))0){strategyFULL_REDUCTION;}else{strategyNO_DISCOUNT;}解法策略接口加一个supports(context)方法让策略自己说明适用条件publicinterfaceDiscountStrategy{booleansupports(OrderContextcontext);// 策略自己声明适用范围BigDecimalcalculate(OrderContextcontext);}// 策略选择变成遍历strategies.stream().filter(s-s.supports(context)).findFirst().orElse(noDiscountStrategy).calculate(context);坑 3Context 对象膨胀随着业务增长传入策略的 Context 参数越来越大变成一个包含所有可能字段的上帝对象。解法为不同策略族定义不同的 Context 接口策略只接受它需要的最小上下文。坑 4过度使用策略模式两三个 if-else 不需要上策略模式。策略模式的适用信号分支数量≥ 5且还会继续增加每个分支的逻辑≥ 10 行有明确的扩展需求新渠道、新格式、新算法两三个简单分支直接写 if-else 更清晰。八、常见问题FAQQ策略模式和工厂模式有什么区别A工厂模式关注的是创建什么对象策略模式关注的是用什么算法执行。实际项目中经常结合工厂负责创建策略对象Context 负责调用策略。Q策略模式和状态模式很像怎么区分A策略模式中策略是外部传入的调用方决定用哪个策略策略本身不会互相转换。状态模式中状态是对象内部的对象根据当前状态决定行为状态之间可以互相转换。简单说策略是选择状态是转换。QSpring 里直接用多个 if-else 分支调用不同的 Service和策略模式有什么实质区别A如果你的 if-else 分支没有抽象出统一接口只是调用不同的 Service 方法那就只是用 Spring 管理依赖的 if-else不是策略模式。策略模式的核心是统一接口——所有策略对 Context 表现一致Context 不知道具体哪个实现被调用。Q什么时候该用枚举策略什么时候该用独立类策略A策略数量固定且不会动态扩展不需要第三方插件新增策略→ 枚举策略可能动态扩展新加策略不改代码→ 独立类 Spring 自动注入。九、小结策略模式的核心价值把用什么算法的决策和如何执行算法的实现解耦。三个实践要点Spring 项目用ListStrategy自动注入 Map 路由零手动注册新增策略一键生效策略类必须无状态——成员变量不存运行时数据请求上下文通过参数传递策略接口加supports()方法——让策略自我声明适用范围消灭策略选择层的 if-else下一篇我们聊装饰器模式——当你需要给一个对象添加新功能但又不想修改原始类、也不想用继承时如何优雅地包装它。标签#设计模式 #策略模式 #Strategy #行为型模式 #Java #Spring #消灭if-else #函数式编程 #支付策略 #连接器鉴权 #面向对象 #软件工程