Java 23 种设计模式:从踩坑到精通 | 番外:工厂方法模式 —— 物流处理器创建实战
摘要:工厂方法模式定义创建对象的接口,让子类决定实例化哪一个类,将对象的实例化延迟到子类中进行。本文结合智能物流中“不同物流方式(空运/陆运)创建对应的物流处理器”的场景,完整展示工厂方法的实现,并与简单工厂、抽象工厂深度对比,帮你掌握“子类决定创建”的设计精髓。
📖《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 | 正篇:工厂方法 Factory —— 还在写一坨 if-else 创建对象吗? |当前:番外 · 工厂方法模式 × 物流处理器创建
🔗 返回系列总目录
1. 物流处理器创建的痛点
物流平台支持多种运输方式——空运、陆运、海运等。每种运输方式都有独立的处理流程、费用计算规则和时效要求。如果在一个方法里根据运输类型if-else创建处理器并执行逻辑,代码会迅速膨胀且难以扩展。更麻烦的是,新增一种运输方式(如铁路运输)需要修改所有相关判断。
工厂方法模式的解决思路:为每种运输方式创建独立的工厂类,客户端通过抽象工厂接口获取处理器,新增运输方式只需新增对应的处理器和工厂类,无需修改现有代码。
2. 工厂方法 UML(物流处理器场景)
3. 完整源码实现
3.1 抽象产品:物流处理器接口
/** * 抽象产品:物流处理器接口 * 定义所有物流处理器的通用行为 */publicinterfaceLogisticsHandler{voidprocess();StringgetHandlerInfo();doublecalculateCost(doubleweight);}💬白话:所有物流处理器都必须能走流程、报信息、算运费。
3.2 具体产品A:空运处理器
/** * 具体产品A:空运物流处理器 */publicclassAirLogisticsHandlerimplementsLogisticsHandler{@Overridepublicvoidprocess(){System.out.println("✈️ 空运物流处理流程启动...");System.out.println(" → 1. 货物安检(航空标准)");System.out.println(" → 2. 打板装箱");System.out.println(" → 3. 报关/清关");System.out.println(" → 4. 装机起飞");System.out.println(" → 5. 目的地卸货派送");}@OverridepublicStringgetHandlerInfo(){return"空运物流处理器 [时效:1-3天,适合急件/高价值货物]";}@OverridepublicdoublecalculateCost(doubleweight){if(weight<=1)return50.0;return50.0+(weight-1)*30.0;}}💬白话:空运流程多了安检和报关步骤,计费也更贵——首重 50 元,续重每 kg 30 元。
3.3 具体产品B:陆运处理器
/** * 具体产品B:陆运物流处理器 */publicclassGroundLogisticsHandlerimplementsLogisticsHandler{@Overridepublicvoidprocess(){System.out.println("🚚 陆运物流处理流程启动...");System.out.println(" → 1. 货物揽收");System.out.println(" → 2. 分拣中心分拨");System.out.println(" → 3. 干线运输(高速/铁路)");System.out.println(" → 4. 目的地网点派送");}@OverridepublicStringgetHandlerInfo(){return"陆运物流处理器 [时效:3-7天,适合普通货物/大件]";}@OverridepublicdoublecalculateCost(doubleweight){if(weight<=1)return12.0;return12.0+(weight-1)*5.0;}}💬白话:陆运流程更简单,计费也更便宜——首重 12 元,续重每 kg 5 元。
3.4 抽象工厂:物流处理器工厂接口
/** * 抽象工厂:物流处理器工厂接口 */publicinterfaceLogisticsHandlerFactory{LogisticsHandlercreateHandler();}💬白话:每个工厂只负责创建一种处理器,这是工厂方法的核心——工厂接口只有一个方法。
3.5 具体工厂A:空运处理器工厂
/** * 具体工厂A:空运处理器工厂 */publicclassAirLogisticsHandlerFactoryimplementsLogisticsHandlerFactory{@OverridepublicLogisticsHandlercreateHandler(){System.out.println("[工厂] 创建空运物流处理器...");returnnewAirLogisticsHandler();}}3.6 具体工厂B:陆运处理器工厂
/** * 具体工厂B:陆运处理器工厂 */publicclassGroundLogisticsHandlerFactoryimplementsLogisticsHandlerFactory{@OverridepublicLogisticsHandlercreateHandler(){System.out.println("[工厂] 创建陆运物流处理器...");returnnewGroundLogisticsHandler();}}3.7 客户端:物流系统
/** * 客户端:物流系统 * 只依赖抽象工厂和抽象产品 */publicclassLogisticsClient{publicvoidshipOrder(StringtransportType,doubleweight){System.out.println("\n"+"═".repeat(60));System.out.println("【物流订单】运输方式:"+transportType+",重量:"+weight+"kg");System.out.println("═".repeat(60));// 根据运输方式选择对应的工厂LogisticsHandlerFactoryfactory;if("air".equalsIgnoreCase(transportType)){factory=newAirLogisticsHandlerFactory();}else{factory=newGroundLogisticsHandlerFactory();}// 通过工厂方法创建处理器(客户端不直接new具体产品)LogisticsHandlerhandler=factory.createHandler();System.out.println("\n--- 处理器信息 ---");System.out.println(handler.getHandlerInfo());System.out.println("--- 运费计算 ---");System.out.printf("运费:%.2f 元\n",handler.calculateCost(weight));System.out.println("--- 处理流程 ---");handler.process();}publicstaticvoidmain(String[]args){System.out.println("=== 工厂方法模式:智能物流处理器创建 ===");LogisticsClientclient=newLogisticsClient();client.shipOrder("air",10.5);client.shipOrder("ground",10.5);}}💬白话:客户端虽然仍需
if-else选择工厂(实际项目中通常配合简单工厂 + 配置来解决),但不直接new具体产品。新增运输方式只需新增处理器和工厂,客户端的选择逻辑可以集中到配置中。
4. 运行结果
=== 工厂方法模式:智能物流处理器创建 === ════════════════════════════════════════════════════════════ 【物流订单】运输方式:air,重量:10.5kg ════════════════════════════════════════════════════════════ [工厂] 创建空运物流处理器... --- 处理器信息 --- 空运物流处理器 [时效:1-3天,适合急件/高价值货物] --- 运费计算 --- 运费:335.00 元 --- 处理流程 --- ✈️ 空运物流处理流程启动... → 1. 货物安检(航空标准) → 2. 打板装箱 → 3. 报关/清关 → 4. 装机起飞 → 5. 目的地卸货派送 ════════════════════════════════════════════════════════════ 【物流订单】运输方式:ground,重量:10.5kg ════════════════════════════════════════════════════════════ [工厂] 创建陆运物流处理器... --- 处理器信息 --- 陆运物流处理器 [时效:3-7天,适合普通货物/大件] --- 运费计算 --- 运费:59.50 元 --- 处理流程 --- 🚚 陆运物流处理流程启动... → 1. 货物揽收 → 2. 分拣中心分拨 → 3. 干线运输(高速/铁路) → 4. 目的地网点派送5. 核心角色回顾
| 角色 | 职责 | 对应代码 |
|---|---|---|
| Product | 定义产品的抽象接口 | LogisticsHandler |
| ConcreteProduct | 实现具体产品 | AirLogisticsHandler/GroundLogisticsHandler |
| Factory | 声明工厂方法 | LogisticsHandlerFactory |
| ConcreteFactory | 实现工厂方法,创建具体产品 | AirLogisticsHandlerFactory/GroundLogisticsHandlerFactory |
| Client | 依赖抽象工厂获取产品 | LogisticsClient |
6. 工厂方法 vs 简单工厂 vs 抽象工厂
| 对比项 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 工厂结构 | 一个具体工厂类 | 抽象工厂接口 + 多个子类 | 抽象工厂接口 + 多个子类 |
| 创建方式 | 静态方法,通过参数判断 | 子类重写工厂方法 | 子类重写多个工厂方法 |
| 产品数量 | 多种产品 | 一种产品 | 一组产品(产品族) |
| 扩展性 | 新增产品需修改工厂(违反OCP) | 新增产品只需新增工厂子类(符合OCP) | 新增产品族符合OCP,新增产品类型违反OCP |
| GoF 收录 | 否 | 是 | 是 |
7. 工厂方法的“坑”与优化
| 问题 | 解决方案 |
|---|---|
| 类爆炸(每产品一工厂) | 产品种类很少时用简单工厂,种类多且变化频繁时用工厂方法 |
| 客户端仍需知道具体工厂类 | 配合简单工厂 + 配置文件,将工厂选择集中管理 |
| 不适合产品间有强关联的场景 | 升级为抽象工厂 |
8. 六大设计原则体现
| 原则 | 体现 |
|---|---|
| 单一职责 | 工厂只负责创建,处理器只负责业务 |
| 开闭原则 | 新增运输方式无需修改现有代码 |
| 里氏替换 | 所有处理器可替换LogisticsHandler接口 |
| 依赖倒置 | 客户端依赖LogisticsHandlerFactory和LogisticsHandler抽象 |
| 接口隔离 | 工厂接口只有一个createHandler()方法 |
| 迪米特法则 | 客户端只与抽象工厂和抽象产品交互 |
附 工厂方法 UML源码(物流处理器场景)
@startuml title Java 23 种设计模式:从踩坑到精通 footer 折哥 | 智能物流与Java实战 ' 1. 全局样式配置 skinparam backgroundColor #FEFEFE skinparam shadowing false skinparam classBorderColor #333333 skinparam classFontColor #1A1A1A skinparam classFontSize 14 skinparam noteFontSize 12 skinparam noteFontColor #555555 skinparam arrowColor #555555 skinparam classBackgroundColor #F9F9F9 ' 2. 抽象产品:物流处理器 interface LogisticsHandler { + void process() + String getHandlerInfo() + double calculateCost(double weight) } note right of LogisticsHandler <b>抽象产品(Product)</b> -- 物流处理器接口,定义所有物流方式 的通用处理行为 end note ' 3. 具体产品A:空运处理器 class AirLogisticsHandler implements LogisticsHandler { + void process() + String getHandlerInfo() + double calculateCost(double weight) } note right of AirLogisticsHandler <b>具体产品A(ConcreteProductA)</b> -- 空运物流处理器:处理航空运输的 全流程业务逻辑 end note ' 4. 具体产品B:陆运处理器 class GroundLogisticsHandler implements LogisticsHandler { + void process() + String getHandlerInfo() + double calculateCost(double weight) } note right of GroundLogisticsHandler <b>具体产品B(ConcreteProductB)</b> -- 陆运物流处理器:处理公路/铁路 运输的全流程业务逻辑 end note ' 5. 抽象工厂:物流处理器工厂 interface LogisticsHandlerFactory { + LogisticsHandler createHandler() } note right of LogisticsHandlerFactory <b>抽象工厂(Factory)</b> -- 声明创建物流处理器的工厂方法, 由子类决定具体创建哪个处理器 end note ' 6. 具体工厂A:空运处理器工厂 class AirLogisticsHandlerFactory implements LogisticsHandlerFactory { + LogisticsHandler createHandler() } note right of AirLogisticsHandlerFactory <b>具体工厂A(ConcreteFactoryA)</b> -- 负责创建空运处理器实例, 客户端无需知道具体产品类 end note ' 7. 具体工厂B:陆运处理器工厂 class GroundLogisticsHandlerFactory implements LogisticsHandlerFactory { + LogisticsHandler createHandler() } note right of GroundLogisticsHandlerFactory <b>具体工厂B(ConcreteFactoryB)</b> -- 负责创建陆运处理器实例, 客户端无需知道具体产品类 end note ' 8. 客户端 class LogisticsClient { + void shipOrder(String transportType, double weight) } note right of LogisticsClient <b>客户端(Client)</b> -- 只依赖抽象工厂和抽象产品, 通过工厂方法获取处理器实例 end note ' 9. 关系连线 LogisticsHandler <|.. AirLogisticsHandler : 实现 LogisticsHandler <|.. GroundLogisticsHandler : 实现 LogisticsHandlerFactory <|.. AirLogisticsHandlerFactory : 实现 LogisticsHandlerFactory <|.. GroundLogisticsHandlerFactory : 实现 AirLogisticsHandlerFactory ..> AirLogisticsHandler : 创建 GroundLogisticsHandlerFactory ..> GroundLogisticsHandler : 创建 LogisticsClient ..> LogisticsHandlerFactory : 使用 LogisticsClient ..> LogisticsHandler : 调用 @enduml🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
- 开篇:系列介绍与目录
- 正篇:工厂方法 Factory —— 还在写一坨 if-else 创建对象吗?
- 当前:番外 · 工厂方法模式 × 物流处理器创建(你在这里)
- 创建型模式汇总
- 结构型模式汇总
- 行为型模式汇总
🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》、《电商多平台电子面单对接实战》。技术相通,思路可鉴。