设计模式-工厂模式:解耦对象创建的设计艺术
工厂模式详解:从制鞋厂看对象创建的艺术
如果把编程比作生产产品,那对象创建就像工厂生产商品。想象一下:如果一家鞋厂没有生产线规划,工人想到什么鞋就做什么鞋,原材料随意堆放,客户要一双运动鞋却给了皮鞋——这样的工厂注定混乱低效。
编程中直接用 new
关键字创建对象,就像这家混乱的鞋厂。而工厂模式,就是给鞋厂设计标准化生产线的“管理体系”。本文将通过“制鞋厂”的现实场景,通俗讲解简单工厂、工厂方法、抽象工厂三种模式的原理与应用。
一、为什么需要“工厂”?——从手工制鞋到标准化生产
假设你是一家小型鞋厂老板,一开始只有两种产品:运动鞋和皮鞋。工人直接手工制作,流程如下(对应代码直接 new
对象):
// 手工制鞋:客户要什么鞋,直接手工做
public class ShoeStore {public Shoe sellShoe(String type) {Shoe shoe;// 客户要运动鞋,就手工做一双if ("sports".equals(type)) {shoe = new SportsShoe();shoe.prepare(); // 准备材料:橡胶底、透气网布shoe.make(); // 制作:缝合、定型}// 客户要皮鞋,再手工做一双else if ("leather".equals(type)) {shoe = new LeatherShoe();shoe.prepare(); // 准备材料:牛皮、橡胶底shoe.make(); // 制作:裁剪、缝制}else {throw new RuntimeException("不生产这种鞋");}return shoe;}
}
随着订单增多,问题逐渐暴露:
- 工人太累:鞋店(
ShoeStore
)既要接待客户,又要负责做鞋,违背“专人专事”(单一职责原则)。 - 扩展困难:想新增“凉鞋”时,要修改鞋店的制作流程(
sellShoe
方法),可能影响已有鞋类的生产(违背开闭原则)。 - 质量不稳定:不同工人做的同一款鞋工艺不一,比如运动鞋的橡胶底厚度不统一。
解决办法很简单:建一条标准化生产线(工厂),专门负责做鞋,鞋店只负责卖鞋。这就是工厂模式的核心思想——将“做鞋(创建对象)”和“卖鞋(使用对象)”分离。
二、简单工厂:小型制鞋流水线
场景模拟
你的鞋厂规模扩大,决定建一条流水线(简单工厂),专门负责生产运动鞋和皮鞋。流水线根据订单类型(参数)决定生产哪种鞋,工人只需按流程操作。
模式定义
简单工厂是一个集中化的生产车间,通过接收“产品类型”参数,决定生产哪种产品。客户端只需告诉工厂“要什么”,无需知道“怎么做”。
代码实现(制鞋场景)
// 1. 抽象产品:鞋子(定义所有鞋的通用属性)
public abstract class Shoe {protected String name; // 鞋名protected String sole; // 鞋底材料protected String upper; // 鞋面材料// 抽象方法:准备材料(不同鞋材料不同)public abstract void prepare();// 通用方法:制作(所有鞋制作流程类似)public void make() {System.out.println("制作" + name + ":缝合鞋底与鞋面");}// 通用方法:包装public void box() {System.out.println("将" + name + "装入包装盒");}
}// 2. 具体产品:运动鞋
public class SportsShoe extends Shoe {public SportsShoe() {name = "运动鞋";}@Overridepublic void prepare() {sole = "橡胶底(防滑)";upper = "透气网布";System.out.println("准备" + name + "材料:" + sole + "、" + upper);}
}// 2. 具体产品:皮鞋
public class LeatherShoe extends Shoe {public LeatherShoe() {name = "皮鞋";}@Overridepublic void prepare() {sole = "橡胶底(耐磨)";upper = "牛皮";System.out.println("准备" + name + "材料:" + sole + "、" + upper);}
}// 3. 简单工厂:制鞋流水线
public class ShoeFactory {// 根据订单类型生产鞋子public static Shoe createShoe(String type) {Shoe shoe = null;if ("sports".equals(type)) {shoe = new SportsShoe();} else if ("leather".equals(type)) {shoe = new LeatherShoe();} else {throw new RuntimeException("不生产" + type + "类型的鞋");}// 工厂统一完成制作流程(准备→制作→包装)shoe.prepare();shoe.make();shoe.box();return shoe;}
}// 4. 客户端:鞋店(只负责卖鞋,不关心制作)
public class ShoeStore {public Shoe sellShoe(String type) {System.out.println("客户下单:" + type + "鞋");// 直接从工厂拿货,无需自己做Shoe shoe = ShoeFactory.createShoe(type);System.out.println("客户取走" + shoe.name + "\n");return shoe;}public static void main(String[] args) {ShoeStore store = new ShoeStore();store.sellShoe("sports"); // 卖运动鞋store.sellShoe("leather"); // 卖皮鞋}
}
运行结果
客户下单:sports鞋
准备运动鞋材料:橡胶底(防滑)、透气网布
制作运动鞋:缝合鞋底与鞋面
将运动鞋装入包装盒
客户取走运动鞋客户下单:leather鞋
准备皮鞋材料:橡胶底(耐磨)、牛皮
制作皮鞋:缝合鞋底与鞋面
将皮鞋装入包装盒
客户取走皮鞋
解决的问题
- 鞋店(
ShoeStore
)彻底解放,只负责销售,不用关心制作流程。 - 制作流程标准化(所有鞋都经过“准备→制作→包装”),质量更稳定。
- 新增鞋类时,鞋店代码无需修改(符合开闭原则)。
三、工厂方法:按产品类型分设车间
场景升级
你的鞋厂名气越来越大,决定新增“凉鞋”产品线。但问题来了:运动鞋需要防滑测试,皮鞋需要皮质检测,凉鞋需要防水测试——简单工厂的一条流水线无法满足不同产品的特殊流程,效率低下。
解决办法:按产品类型分设车间——运动鞋车间专门生产运动鞋,皮鞋车间专门生产皮鞋,每个车间有自己的特殊工艺。
模式定义
工厂方法为每种产品设立专属工厂,每个工厂只生产一种产品,并负责该产品的全部生产流程(包括特殊工艺)。客户端通过选择不同工厂来获取对应产品。
代码实现(分车间生产)
// 1. 抽象产品:鞋子(同上,新增特殊工艺方法)
public abstract class Shoe {// ... 原有代码 ...public abstract void specialProcess(); // 特殊工艺(不同鞋不一样)
}// 2. 具体产品:运动鞋(新增防滑测试)
public class SportsShoe extends Shoe {// ... 原有代码 ...@Overridepublic void specialProcess() {System.out.println("运动鞋特殊工艺:防滑测试");}
}// 2. 具体产品:皮鞋(新增皮质检测)
public class LeatherShoe extends Shoe {// ... 原有代码 ...@Overridepublic void specialProcess() {System.out.println("皮鞋特殊工艺:皮质检测");}
}// 3. 抽象工厂:鞋厂车间接口(定义生产流程)
public interface ShoeFactory {Shoe produceShoe(); // 生产鞋子
}// 4. 具体工厂:运动鞋车间
public class SportsShoeFactory implements ShoeFactory {@Overridepublic Shoe produceShoe() {System.out.println("===== 运动鞋车间 =====");Shoe shoe = new SportsShoe();shoe.prepare();shoe.make();shoe.specialProcess(); // 执行运动鞋特殊工艺shoe.box();return shoe;}
}// 4. 具体工厂:皮鞋车间
public class LeatherShoeFactory implements ShoeFactory {@Overridepublic Shoe produceShoe() {System.out.println("===== 皮鞋车间 =====");Shoe shoe = new LeatherShoe();shoe.prepare();shoe.make();shoe.specialProcess(); // 执行皮鞋特殊工艺shoe.box();return shoe;}
}// 5. 客户端:鞋店(根据客户需求选择车间)
public class ShoeStore {// 传入具体工厂,而非产品类型public Shoe sellShoe(ShoeFactory factory) {System.out.println("客户下单,通知对应车间生产");Shoe shoe = factory.produceShoe();System.out.println("客户取走" + shoe.name + "\n");return shoe;}public static void main(String[] args) {ShoeStore store = new ShoeStore();// 卖运动鞋:找运动鞋车间store.sellShoe(new SportsShoeFactory());// 卖皮鞋:找皮鞋车间store.sellShoe(new LeatherShoeFactory());}
}
运行结果
客户下单,通知对应车间生产
===== 运动鞋车间 =====
准备运动鞋材料:橡胶底(防滑)、透气网布
制作运动鞋:缝合鞋底与鞋面
运动鞋特殊工艺:防滑测试
将运动鞋装入包装盒
客户取走运动鞋客户下单,通知对应车间生产
===== 皮鞋车间 =====
准备皮鞋材料:橡胶底(耐磨)、牛皮
制作皮鞋:缝合鞋底与鞋面
皮鞋特殊工艺:皮质检测
将皮鞋装入包装盒
客户取走皮鞋
新增产品的优势
当需要新增“凉鞋”时,只需添加两个类,原有代码完全不用改:
// 新增具体产品:凉鞋
public class Sandal extends Shoe {public Sandal() { name = "凉鞋"; }@Overridepublic void prepare() { /* 准备塑料鞋底和帆布 */ }@Overridepublic void specialProcess() {System.out.println("凉鞋特殊工艺:防水测试");}
}// 新增具体工厂:凉鞋车间
public class SandalFactory implements ShoeFactory {@Overridepublic Shoe produceShoe() {System.out.println("===== 凉鞋车间 =====");// ... 生产流程 ...return new Sandal();}
}// 客户端直接使用新车间
store.sellShoe(new SandalFactory()); // 无需修改鞋店代码
这完美符合“开闭原则”——扩展开放,修改关闭。
四、抽象工厂:按品牌设立生产线
场景再升级
你的鞋厂开始代工国际品牌,比如耐克(Nike)和阿迪达斯(Adidas)。每个品牌不仅有自己的运动鞋,还有配套的鞋盒、标签——这些产品需要风格统一(比如耐克的logo、配色)。
此时需要解决的问题:确保同一品牌的系列产品风格一致(如耐克运动鞋+耐克鞋盒+耐克标签)。这就是抽象工厂要解决的“产品族”问题。
模式定义
抽象工厂用于生产一系列相互关联的产品族(如“耐克品牌的鞋+鞋盒+标签”)。每个具体工厂对应一个品牌,负责生产该品牌的所有配套产品,确保风格统一。
代码实现(品牌代工场景)
// 1. 抽象产品族:鞋子、鞋盒、标签(同一品牌需风格统一)
public interface Shoe { void showBrand(); }
public interface ShoeBox { void showBrand(); }
public interface Label { void showBrand(); }// 2. 具体产品族1:耐克(Nike)
public class NikeShoe implements Shoe {@Overridepublic void showBrand() { System.out.println("耐克运动鞋(勾形logo)"); }
}
public class NikeBox implements ShoeBox {@Overridepublic void showBrand() { System.out.println("耐克鞋盒(红色)"); }
}
public class NikeLabel implements Label {@Overridepublic void showBrand() { System.out.println("耐克标签(材质:纸质)"); }
}// 2. 具体产品族2:阿迪达斯(Adidas)
public class AdidasShoe implements Shoe {@Overridepublic void showBrand() { System.out.println("阿迪运动鞋(三道杠logo)"); }
}
public class AdidasBox implements ShoeBox {@Overridepublic void showBrand() { System.out.println("阿迪鞋盒(蓝色)"); }
}
public class AdidasLabel implements Label {@Overridepublic void showBrand() { System.out.println("阿迪标签(材质:布质)"); }
}// 3. 抽象工厂:品牌生产线(定义生产所有配套产品的接口)
public interface BrandFactory {Shoe createShoe();ShoeBox createShoeBox();Label createLabel();
}// 4. 具体工厂1:耐克生产线
public class NikeFactory implements BrandFactory {@Overridepublic Shoe createShoe() { return new NikeShoe(); }@Overridepublic ShoeBox createShoeBox() { return new NikeBox(); }@Overridepublic Label createLabel() { return new NikeLabel(); }
}// 4. 具体工厂2:阿迪生产线
public class AdidasFactory implements BrandFactory {@Overridepublic Shoe createShoe() { return new AdidasShoe(); }@Overridepublic ShoeBox createShoeBox() { return new AdidasBox(); }@Overridepublic Label createLabel() { return new AdidasLabel(); }
}// 5. 客户端:代工厂组装车间(按品牌组装产品)
public class AssemblyLine {private Shoe shoe;private ShoeBox box;private Label label;// 传入品牌工厂,确保所有产品风格统一public AssemblyLine(BrandFactory factory) {this.shoe = factory.createShoe();this.box = factory.createShoeBox();this.label = factory.createLabel();}public void assemble() {System.out.println("开始组装品牌产品:");shoe.showBrand();box.showBrand();label.showBrand();System.out.println("组装完成\n");}public static void main(String[] args) {// 组装耐克产品(所有组件都是耐克)AssemblyLine nikeLine = new AssemblyLine(new NikeFactory());nikeLine.assemble();// 组装阿迪产品(所有组件都是阿迪)AssemblyLine adidasLine = new AssemblyLine(new AdidasFactory());adidasLine.assemble();}
}
运行结果
开始组装品牌产品:
耐克运动鞋(勾形logo)
耐克鞋盒(红色)
耐克标签(材质:纸质)
组装完成开始组装品牌产品:
阿迪运动鞋(三道杠logo)
阿迪鞋盒(蓝色)
阿迪标签(材质:布质)
组装完成
核心价值
抽象工厂确保了同一产品族的兼容性——你永远不会得到“耐克运动鞋+阿迪鞋盒”的组合,这在需要统一风格的场景(如品牌代工、跨平台UI组件)中至关重要。
五、三种工厂模式的对比:该用哪一种?
模式类型 | 现实场景类比 | 核心优势 | 适合情况 |
---|---|---|---|
简单工厂 | 小型流水线(一条线生产多种鞋) | 实现简单,集中管理创建逻辑 | 产品少且变化不频繁(如只有2-3种鞋) |
工厂方法 | 按产品分车间(运动鞋车间、皮鞋车间) | 扩展灵活,符合开闭原则 | 产品多或经常新增产品(如不断推出新鞋型) |
抽象工厂 | 按品牌分生产线(耐克线、阿迪线) | 保证产品族的一致性 | 需要生产系列配套产品(如鞋+鞋盒+标签) |
用一句话总结:
- 简单工厂是“一厂多品”
- 工厂方法是“一厂一品”
- 抽象工厂是“一厂一族”
六、工厂模式的设计本质
从制鞋厂的例子可以看出,工厂模式的核心不是“创建工厂类”,而是建立“创建者”与“产品”之间的隔离带:
- 让“创建者”(工厂)专注于“如何生产”(封装变化的创建逻辑)。
- 让“使用者”(客户端)专注于“如何使用”(依赖稳定的抽象接口)。
这种隔离带来了两大好处:
- 灵活性:更换产品时,只需更换工厂,无需修改使用者代码(如鞋店换个车间就能卖新鞋)。
- 稳定性:产品创建逻辑的变化不会影响使用者(如车间改进工艺,鞋店卖鞋流程不变)。
总结:从鞋厂到代码的启示
工厂模式就像制鞋厂的管理体系:
- 小作坊(简单场景)用简单工厂,快速高效。
- 多产品线的中型工厂用工厂方法,灵活扩展。
- 生产系列产品的大型企业用抽象工厂,保证一致性。
在代码中,当发现“对象创建逻辑散落在业务代码中”“新增产品需要修改多处代码”时,不妨想想制鞋厂的例子——是时候引入工厂模式,给你的代码建一条“标准化生产线”了。
Studying will never be ending.
▲如有纰漏,烦请指正~~