当前位置: 首页 > news >正文

干掉成山的 if-else:工厂造、策略选,一文讲透两个模式的配合

大家好,我是晚安code。

你大概率见过这种支付代码:一个 pay() 方法里 if/else 塞了 6 个分支,微信、支付宝、银行卡各占一段,每加一个渠道就再往里堆一块。问起来永远一句「赶进度,先这样吧」。

这种「先这样」最后都会变成「只能这样」。工厂模式和策略模式这俩,一个给「创建对象」解耦,一个给「选择算法」解耦,配合起来正好干掉这种分支地狱。这篇我从生活类比一路讲到 Spring 源码,一次讲透。先收藏,慢慢看。

一、先搞清楚:设计模式到底是什么,为什么要学

设计模式不是炫技,是前人踩坑踩出来的「套路」,让你写出来的代码别人接手时不用骂街。

设计模式(Design Pattern):前人针对反复出现的软件设计问题,总结出的可复用解决方案。你可以把它理解成「代码界的菜谱」——同一道菜(同一个问题),照着成熟配方做,不会翻车。

那为什么非要学?我给你三个最实在的理由:第一是复用,别人趟过的坑你不用再趟一遍;第二是沟通,团队里说一句「这里用工厂模式」,大家都懂你在说什么,省下半小时画图;第三是可维护,符合下面这条原则的代码,加功能不用动老逻辑。

开闭原则(Open-Closed Principle,OCP):软件实体应该「对扩展开放,对修改关闭」——加新功能时尽量加新代码,而不是去改已经在跑的老代码。这条原则是后面两个模式共同的目标。

打个生活化的比方:盖楼不会每栋都从和泥开始,你会用标准化的图纸和预制件;设计模式就是代码世界里的预制件图纸。GoF 23 种模式里,工厂模式和策略模式是出现频率最高的两个,下面我一个个拆给你看。

二、工厂模式:把「new」这件事集中管起来

工厂模式解决的核心痛点是——对象该怎么创建,又不让调用方关心创建细节。

工厂模式(Factory Pattern):把对象的创建过程封装进一个「工厂」角色里,调用方只要告诉工厂「我要什么」,不用自己 new、不用关心构造细节。类比就是去餐厅点餐:你喊一句「来份宫保鸡丁」,后厨怎么切、怎么炒、食材哪来的,你一概不管,上菜就完事。

工厂模式其实分三种,别被名字绕晕,记住区别就行:

  • 简单工厂:一个工厂方法里 switch 一下,返回不同对象。最常用,但严格说不算 GoF 23 种。
  • 工厂方法:每个产品配一个工厂类,加新产品只加新类,完全符合开闭原则。
  • 抽象工厂:管一「族」产品,比如一套前端组件要同时出深色版和浅色版。

放到生活里再秒懂一遍:你点外卖,App 后面是个「订单工厂」,不管它最终走的是哪家配送接口,你只管点;寄件时选顺丰还是圆通,对你来说都是「叫个快递」,背后是不同的快递工厂在创建运单。

开发里这类场景太常见了:根据配置创建不同数据库连接(MySQL / PostgreSQL)、日志组件按需切换(控制台 / 文件 / 远程)、根据消息类型创建不同处理器。核心都是「创建逻辑会变,但调用方不想跟着变」。

看一段最朴素的简单工厂(Java,示例):

// 示例:把支付渠道的创建集中到工厂里publicinterfacePayChannel{voidpay(BigDecimalamount);}publicclassPayFactory{publicstaticPayChannelcreate(Stringtype){returnswitch(type){case"wechat"->newWechatPay();case"alipay"->newAlipayPay();default->thrownewIllegalArgumentException("未知渠道: "+type);};}}// 调用方:不关心怎么 new,只要结果PayChannelch=PayFactory.create("alipay");ch.pay(BigDecimal.valueOf(99));

调用方手里只有PayChannel接口,根本不知道AlipayPay长啥样。工厂挡在客户端和具体产品中间,客户端只依赖产品接口,加新产品时改工厂、不动调用方:

到了 Spring,工厂更是遍地都是。BeanFactoryApplicationContext本身就是个超级大工厂——你调一句getBean("xxx"),它帮你创建并返回对象,这就是简单工厂的巨型版。FactoryBean接口更典型:你实现getObject()方法自己控制怎么造 Bean,Spring 最终拿到的就是你造出来的对象。集成 MyBatis、Dubbo 时那些代理对象,基本都是靠FactoryBean造出来的。

JDK 里也不少:Calendar.getInstance()NumberFormat.getInstance()DriverManager.getConnection(),骨子里都是工厂。

三、策略模式:用「换算法」干掉成山的 if-else

策略模式解决的核心痛点是——同一件事有多种做法,怎么做到想换就换、还不碰老代码。

策略模式(Strategy Pattern):把一组做同一件事的不同算法,各自封装成独立的类,它们实现同一个接口,因此可以互相替换。类比就是导航 App 算路线:「最快」「最省钱」「避开拥堵」就是三个策略,目的地不变,换个策略就走不同的路。

放到生活里再秒懂:去收银台结账,现金、刷卡、扫码是三种「支付策略」,你选哪个都行,但最终都是「完成付款」这一件事。开发里这类场景同样密集:促销打折(满减 / 打折 / 用券)、风控校验(不同等级用户走不同规则)、消息推送(短信 / 邮件 / 站内信)。

先看一段反面教材,这种代码你大概率写过:

// 示例:反面教材——越加越长的折扣方法publicBigDecimalcalc(Stringtype,BigDecimalprice){if("fullReduction".equals(type)){// 满减returnprice.compareTo(HUNDRED)>0?price.subtract(TEN):price;}elseif("discount".equals(type)){// 打折returnprice.multiply(newBigDecimal("0.8"));}elseif("coupon".equals(type)){// 优惠券returnprice.subtract(COUPON);}returnprice;}

每加一种活动,就得改这个方法、加一个分支,几百行挤在一起,老逻辑被反复触碰——典型的违反开闭原则。用策略模式重构一下:

// 示例:策略接口 + 每个算法独立成类publicinterfaceDiscountStrategy{BigDecimalapply(BigDecimalprice);}publicclassDiscountStrategy8implementsDiscountStrategy{publicBigDecimalapply(BigDecimalprice){returnprice.multiply(newBigDecimal("0.8"));}}// 调用方持有策略,想换随时换,互不影响DiscountStrategys=newDiscountStrategy8();s.apply(BigDecimal.valueOf(100));

两种写法摆一起对比,差距很明显:

对比项一坨 if-else策略模式
加新算法改老方法、加分支新增一个策略类,老代码不动
可读性几百行挤一起每个算法独立,各管各的
单元测试分支互相影响,难测每个策略单独测
开闭原则不符合符合

可能有人会问:策略模式不就是把 if-else 换成map.get()吗,有啥意义?

形式上像,本质不同。map 只是查找手段;真正的意义是「每个算法独立成类、可单独测试、可热插拔」。更关键的是,它把「选哪个」和「怎么算」拆开了——选哪个交给调用方或工厂,怎么算交给策略自己,职责一清二楚。

JDK 里最经典的策略,是ThreadPoolExecutor的拒绝策略RejectedExecutionHandler:线程池任务满了怎么办?它给你四个策略——AbortPolicy(抛异常)、CallerRunsPolicy(让调用线程自己跑)、DiscardPolicy(直接丢)、DiscardOldestPolicy(丢最老的任务)。换一个 handler,线程池行为完全不同,这就是教科书级的策略模式。

Spring 里也有:Resource接口,ClassPathResourceFileSystemResourceUrlResource都是它的实现,访问不同位置的资源用不同策略,Resource这层抽象把它们统一了起来。

四、为什么它俩总是一起出现:工厂造、策略选

单用策略模式有个尴尬——调用方还得自己 new 具体策略,结果又耦合回去了;工厂模式正好补上这一刀。

你看上面折扣的例子,调用方写的是new DiscountStrategy8(),它又得知道具体类名,换策略就得改这行 new。这就好比导航 App 让你自己手写每条路线的代码,那还策略个啥。

把工厂请进来就顺了:调用方只说「我要打折策略」,工厂负责把对应的策略对象造出来递过去。策略管「算法怎么算」,工厂管「对象怎么来」,分工一明确,if-else 就彻底没了落脚点。请求带着类型进来,上下文找工厂要策略,工厂从一堆策略里取对的那个,统一调 pay:

开发里最经典的配合场景就是支付系统:微信、支付宝、银行卡是三个支付策略,再加新渠道(比如 Apple Pay)只是加一个策略类,老代码一行不改。下面是后端最常用的 Spring 写法(以 Spring 6.x、JDK 17 为例,2026 年实测语法):

// 示例:Spring 自动注入 Map,本质就是个策略工厂publicinterfacePayStrategy{voidpay(BigDecimalamount);}@Service("wechat")publicclassWechatPayimplementsPayStrategy{/* ... */}@Service("alipay")publicclassAlipayPayimplementsPayStrategy{/* ... */}@ComponentpublicclassPayContext{@AutowiredprivateMap<String,PayStrategy>strategyMap;// Spring 按 Bean 名自动注入publicvoidpay(Stringtype,BigDecimalamount){PayStrategys=strategyMap.get(type);if(s==null)thrownewIllegalArgumentException("不支持: "+type);s.pay(amount);}}

这里的Map<String, PayStrategy>就是 Spring 帮你建好的工厂——所有实现了PayStrategy的 Bean 自动塞进 Map,key 是 Bean 名。加 Apple Pay?写一个@Service("applepay")的类,结束。没有 if-else、没有 switch,完全符合开闭原则。

可能有人会问:这套在非 Spring 项目里还能用吗?

能。没有 Spring,你就自己写个工厂类,在构造方法或静态块里手动map.put("wechat", new WechatPay())注册策略,效果一样。

Spring 只是把「手动注册」这步用依赖注入自动化了,省的是体力,不是思想。

在我看来,工厂模式和策略模式不是两个孤立的面试考点,而是一对搭档:工厂把「创建」关进笼子,策略把「选择」拆成零件,合在一起,你的代码才能做到加功能不动老逻辑。面试官爱问、Spring 源码里遍地都是,恰恰说明它们是真的好用,而不只是八股。下一篇我会聊聊「策略 + 工厂 + 模板方法」三件套,以及怎么用枚举把策略注册也收编掉,感兴趣的话先点个关注。


我是晚安code,持续分享编程干货。觉得有用的话记得点赞收藏和关注~也欢迎在评论区聊聊:你项目里那些成山的 if-else,最后是怎么被你干掉的?

http://www.jsqmd.com/news/1285932/

相关文章:

  • Azure VM代理状态异常排查与修复实战指南
  • 从《水经注》到时空知识图谱:1个Python脚本自动提取2000年水系变迁关系,附实测精度报告(F1=0.93)
  • 基于三菱FX2N PLC的工业洗衣机控制系统设计详解
  • 工业物联网通信模块与MCU选型及安全优化实践
  • 射频巴伦电路设计:从原理到选型与调试实战
  • CTF Web安全入门:从MD5漏洞原理到实战绕过技巧详解
  • 宿迁漏水检测公司推荐-同城防水补漏免砸砖维修-暗管漏水精准定位-卫生间-厨房-屋顶-阳台-地下室渗漏综合治理指南 - 知途管道科技
  • 2026兰州漏水检测公司推荐:同城防水补漏免砸砖维修-暗管漏水精准定位-卫生间-屋顶-阳台-厨房-地下室渗漏综合治理 - 知途管道科技
  • 2026菏泽漏水维修全攻略,卫生间/阳台/外墙/屋顶/地下室对症方案+靠谱商家推荐 - 苏易房屋修缮
  • GSM+GPS模块硬件设计、软件驱动与典型问题排查实战指南
  • 圆周率节背后的硬核玩法:从记忆宫殿到算法实现
  • 【AI自动整理数据终极指南】:20年数据工程师亲授5大落地场景与避坑清单
  • 警惕!新型僵尸网络Dysphoria大范围传播,超20万台设备已沦陷
  • Java并发容器原理与性能优化实践
  • 私有化大模型项目频繁报错、算力失控?企业 AI 网关标准化落地解决方案
  • 基于Arduino与3D打印的智能洗鞋机DIY:从硬件开源到自动化实践
  • 波基础原理相关(AI回答)
  • 云浮漏水检测正规公司推荐-同城防水补漏免砸砖维修-暗管漏水精准定位-卫生间-屋顶-阳台-厨房-地下室渗漏综合治理指南 - 知途管道科技
  • 2026北京漏水检测正规公司推荐-暗管测漏精准定位-卫生间-厨房-屋顶-阳台-地下室防水补漏维修指南 - 知途管道科技
  • LENA-R8与PIC18F46K22在物联网定位与通信中的实践
  • Java文件操作拒绝访问错误:从权限、文件锁到路径问题的全面诊断与解决
  • 基于ESP32与传感器打造情感互动装置:从状态机到拟人化反馈
  • 账实不符越盘越乱?教你3个高效盘点绝招,告别盘亏与库存漏洞!
  • DIY 3D打印线材拉丝机:从原理到实践,实现材料自由
  • 三引物PCR检测法:原理、设计与实战,突变体鉴定的高效解决方案
  • MATLAB Stateflow状态机生成C代码实战:从建模到嵌入式集成
  • ESP32-C6低功耗Wi-Fi小夜灯开发全记录:硬件选型、固件开发与功耗优化实战
  • PcDuino装机预备指南:从镜像烧录到开发环境配置
  • 2026滨州漏水维修全攻略,卫生间/阳台/外墙/屋顶/地下室对症方案+靠谱商家推荐 - 苏易房屋修缮
  • 月球远古磁场揭秘:从阿波罗岩石到行星发电机理论