工厂模式详解:从制鞋厂看对象创建的艺术
如果把编程比作生产产品,那对象创建就像工厂生产商品。想象一下:如果一家鞋厂没有生产线规划,工人想到什么鞋就做什么鞋,原材料随意堆放,客户要一双运动鞋却给了皮鞋------这样的工厂注定混乱低效。
编程中直接用 new
关键字创建对象,就像这家混乱的鞋厂。而工厂模式,就是给鞋厂设计标准化生产线的"管理体系"。本文将通过"制鞋厂"的现实场景,通俗讲解简单工厂、工厂方法、抽象工厂三种模式的原理与应用。
一、为什么需要"工厂"?------从手工制鞋到标准化生产
假设你是一家小型鞋厂老板,一开始只有两种产品:运动鞋和皮鞋。工人直接手工制作,流程如下(对应代码直接 new
对象):
java
// 手工制鞋:客户要什么鞋,直接手工做
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
方法),可能影响已有鞋类的生产(违背开闭原则)。 - 质量不稳定:不同工人做的同一款鞋工艺不一,比如运动鞋的橡胶底厚度不统一。
解决办法很简单:建一条标准化生产线(工厂) ,专门负责做鞋,鞋店只负责卖鞋。这就是工厂模式的核心思想------将"做鞋(创建对象)"和"卖鞋(使用对象)"分离。
二、简单工厂:小型制鞋流水线
场景模拟
你的鞋厂规模扩大,决定建一条流水线(简单工厂),专门负责生产运动鞋和皮鞋。流水线根据订单类型(参数)决定生产哪种鞋,工人只需按流程操作。
模式定义
简单工厂是一个集中化的生产车间,通过接收"产品类型"参数,决定生产哪种产品。客户端只需告诉工厂"要什么",无需知道"怎么做"。
代码实现(制鞋场景)
java
// 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 = "运动鞋";
}
@Override
public void prepare() {
sole = "橡胶底(防滑)";
upper = "透气网布";
System.out.println("准备" + name + "材料:" + sole + "、" + upper);
}
}
// 2. 具体产品:皮鞋
public class LeatherShoe extends Shoe {
public LeatherShoe() {
name = "皮鞋";
}
@Override
public 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
)彻底解放,只负责销售,不用关心制作流程。 - 制作流程标准化(所有鞋都经过"准备→制作→包装"),质量更稳定。
- 新增鞋类时,鞋店代码无需修改(符合开闭原则)。
三、工厂方法:按产品类型分设车间
场景升级
你的鞋厂名气越来越大,决定新增"凉鞋"产品线。但问题来了:运动鞋需要防滑测试,皮鞋需要皮质检测,凉鞋需要防水测试------简单工厂的一条流水线无法满足不同产品的特殊流程,效率低下。
解决办法:按产品类型分设车间------运动鞋车间专门生产运动鞋,皮鞋车间专门生产皮鞋,每个车间有自己的特殊工艺。
模式定义
工厂方法为每种产品设立专属工厂,每个工厂只生产一种产品,并负责该产品的全部生产流程(包括特殊工艺)。客户端通过选择不同工厂来获取对应产品。
代码实现(分车间生产)
java
// 1. 抽象产品:鞋子(同上,新增特殊工艺方法)
public abstract class Shoe {
// ... 原有代码 ...
public abstract void specialProcess(); // 特殊工艺(不同鞋不一样)
}
// 2. 具体产品:运动鞋(新增防滑测试)
public class SportsShoe extends Shoe {
// ... 原有代码 ...
@Override
public void specialProcess() {
System.out.println("运动鞋特殊工艺:防滑测试");
}
}
// 2. 具体产品:皮鞋(新增皮质检测)
public class LeatherShoe extends Shoe {
// ... 原有代码 ...
@Override
public void specialProcess() {
System.out.println("皮鞋特殊工艺:皮质检测");
}
}
// 3. 抽象工厂:鞋厂车间接口(定义生产流程)
public interface ShoeFactory {
Shoe produceShoe(); // 生产鞋子
}
// 4. 具体工厂:运动鞋车间
public class SportsShoeFactory implements ShoeFactory {
@Override
public 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 {
@Override
public 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());
}
}
运行结果
客户下单,通知对应车间生产
===== 运动鞋车间 =====
准备运动鞋材料:橡胶底(防滑)、透气网布
制作运动鞋:缝合鞋底与鞋面
运动鞋特殊工艺:防滑测试
将运动鞋装入包装盒
客户取走运动鞋
客户下单,通知对应车间生产
===== 皮鞋车间 =====
准备皮鞋材料:橡胶底(耐磨)、牛皮
制作皮鞋:缝合鞋底与鞋面
皮鞋特殊工艺:皮质检测
将皮鞋装入包装盒
客户取走皮鞋
新增产品的优势
当需要新增"凉鞋"时,只需添加两个类,原有代码完全不用改:
java
// 新增具体产品:凉鞋
public class Sandal extends Shoe {
public Sandal() { name = "凉鞋"; }
@Override
public void prepare() { /* 准备塑料鞋底和帆布 */ }
@Override
public void specialProcess() {
System.out.println("凉鞋特殊工艺:防水测试");
}
}
// 新增具体工厂:凉鞋车间
public class SandalFactory implements ShoeFactory {
@Override
public Shoe produceShoe() {
System.out.println("===== 凉鞋车间 =====");
// ... 生产流程 ...
return new Sandal();
}
}
// 客户端直接使用新车间
store.sellShoe(new SandalFactory()); // 无需修改鞋店代码
这完美符合"开闭原则"------扩展开放,修改关闭。
四、抽象工厂:按品牌设立生产线
场景再升级
你的鞋厂开始代工国际品牌,比如耐克(Nike)和阿迪达斯(Adidas)。每个品牌不仅有自己的运动鞋,还有配套的鞋盒、标签------这些产品需要风格统一(比如耐克的logo、配色)。
此时需要解决的问题:确保同一品牌的系列产品风格一致(如耐克运动鞋+耐克鞋盒+耐克标签)。这就是抽象工厂要解决的"产品族"问题。
模式定义
抽象工厂用于生产一系列相互关联的产品族(如"耐克品牌的鞋+鞋盒+标签")。每个具体工厂对应一个品牌,负责生产该品牌的所有配套产品,确保风格统一。
代码实现(品牌代工场景)
java
// 1. 抽象产品族:鞋子、鞋盒、标签(同一品牌需风格统一)
public interface Shoe { void showBrand(); }
public interface ShoeBox { void showBrand(); }
public interface Label { void showBrand(); }
// 2. 具体产品族1:耐克(Nike)
public class NikeShoe implements Shoe {
@Override
public void showBrand() { System.out.println("耐克运动鞋(勾形logo)"); }
}
public class NikeBox implements ShoeBox {
@Override
public void showBrand() { System.out.println("耐克鞋盒(红色)"); }
}
public class NikeLabel implements Label {
@Override
public void showBrand() { System.out.println("耐克标签(材质:纸质)"); }
}
// 2. 具体产品族2:阿迪达斯(Adidas)
public class AdidasShoe implements Shoe {
@Override
public void showBrand() { System.out.println("阿迪运动鞋(三道杠logo)"); }
}
public class AdidasBox implements ShoeBox {
@Override
public void showBrand() { System.out.println("阿迪鞋盒(蓝色)"); }
}
public class AdidasLabel implements Label {
@Override
public void showBrand() { System.out.println("阿迪标签(材质:布质)"); }
}
// 3. 抽象工厂:品牌生产线(定义生产所有配套产品的接口)
public interface BrandFactory {
Shoe createShoe();
ShoeBox createShoeBox();
Label createLabel();
}
// 4. 具体工厂1:耐克生产线
public class NikeFactory implements BrandFactory {
@Override
public Shoe createShoe() { return new NikeShoe(); }
@Override
public ShoeBox createShoeBox() { return new NikeBox(); }
@Override
public Label createLabel() { return new NikeLabel(); }
}
// 4. 具体工厂2:阿迪生产线
public class AdidasFactory implements BrandFactory {
@Override
public Shoe createShoe() { return new AdidasShoe(); }
@Override
public ShoeBox createShoeBox() { return new AdidasBox(); }
@Override
public 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.
▲如有纰漏,烦请指正~~