一、核心概念
工厂模式 = "对象的'生产车间'" 核心逻辑:把 "创建对象" 的活儿,从客户端手里抢过来,交给专门的 "工厂类" 干------ 客户端不用知道对象是怎么 new 出来的、内部逻辑是什么,只要告诉工厂 "我要什么",工厂直接把造好的对象扔给你用。
二、解决的核心痛点(为什么不能直接 new,非要用工厂?)
工厂模式解决 3 个核心问题:
- 创建逻辑太复杂,客户端扛不住:如果一个对象创建要写 10 行代码(比如初始化参数、连接资源、校验规则),每个客户端都要写一遍,重复代码爆炸;
- 对象类型可能变,改代码改到吐 :比如原来用
MySQLConnection,后来要换成OracleConnection,如果所有客户端都直接new MySQLConnection(),得一个个改; - 客户端不需要知道 "怎么造",只需要 "怎么用":你买手机不用知道芯片怎么焊、系统怎么装,店里(工厂)给你成品就行 ------ 对象也一样。
三、核心角色
工厂模式的 4 个核心角色,每个角色各司其职,分工明确:
| 角色名称 | 通俗解释(类比现实) | 工厂模式中具体例子(Shape 案例) |
|---|---|---|
| 抽象产品(Abstract Product) | 所有产品的 "通用说明书":规定产品必须有哪些功能 | Shape接口(规定必须有draw()方法) |
| 具体产品(Concrete Product) | 符合说明书的 "具体商品":实现通用功能 | Circle、Rectangle、Square(各自实现draw()) |
| 抽象工厂(Abstract Factory) | 工厂的 "通用营业执照":规定工厂必须能造哪些产品 | (简单工厂没有这个角色,工厂方法 / 抽象工厂才有)比如ShapeFactory接口(规定必须有getShape()方法) |
| 具体工厂(Concrete Factory) | 真正的 "生产车间":按说明书造具体商品 | ShapeFactory类(实现getShape(),造具体形状) |
👉 补充:简单工厂模式没有 "抽象工厂",直接用 "具体工厂" 干所有活(入门款);工厂方法 / 抽象工厂是进阶款,多了 "抽象工厂" 来适配更多产品类型(后面会说)。
四、工作流程
工厂模式的流程比较简单,3 步搞定:
- 客户端提需求 :客户端不自己
new对象,而是找工厂说 "我要一个圆形(CIRCLE)"; - 工厂搞生产 :工厂接收需求,按自己的逻辑(比如判断参数)创建对应的具体产品对象(
new Circle()); - 客户端直接用 :工厂把造好的对象返回给客户端,客户端只需要调用对象的方法(
shape.draw()),完全不管对象是怎么造出来的。
👉 工厂模式流程是:客户端发需求→工厂创建产品→返回产品→客户端使用。
五、代码示例
第一步:定义 "抽象产品"(通用说明书)
// 抽象产品:Shape接口(规定所有形状必须能"画")
public interface Shape {
void draw(); // 通用功能:画自己
}
第二步:定义 "具体产品"(具体商品)
// 具体产品1:圆形(符合Shape说明书,实现draw())
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("画了一个圆形");
}
}
// 具体产品2:矩形(同理)
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("画了一个矩形");
}
}
// 具体产品3:正方形(同理)
public class Square implements Shape {
@Override
public void draw() {
System.out.println("画了一个正方形");
}
}
第三步:定义 "具体工厂"(生产车间)
// 具体工厂:ShapeFactory(负责造所有Shape产品)
public class ShapeFactory {
// 生产方法:接收需求(形状名称),返回造好的产品
public Shape getShape(String shapeType) {
if (shapeType == null) {
return null; // 没需求,不生产
}
// 按需求造对应产品(工厂的核心生产逻辑)
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
} else if (shapeType.equalsIgnoreCase("SQUARE")) {
return new Square();
}
return null; // 没有对应产品
}
}
第四步:客户端调用
public class FactoryDemo {
public static void main(String[] args) {
// 1. 拿到工厂
ShapeFactory shapeFactory = new ShapeFactory();
// 2. 提需求:要圆形,工厂返回产品(不用自己new Circle())
Shape circle = shapeFactory.getShape("CIRCLE");
circle.draw(); // 用产品:画圆形
// 3. 提需求:要矩形
Shape rectangle = shapeFactory.getShape("RECTANGLE");
rectangle.draw(); // 用产品:画矩形
// 4. 提需求:要正方形
Shape square = shapeFactory.getShape("SQUARE");
square.draw(); // 用产品:画正方形
}
}
运行结果
画了一个圆形
画了一个矩形
画了一个正方形
六、工厂模式的 3 个变种
就像责任链有 "单向链、环形链",工厂模式也有 3 个核心变种,核心都是 "创建与使用分离",只是灵活度不同:
| 变种类型 | 通俗解释(类比现实) | 核心特点 | 适用场景 |
|---|---|---|---|
| 简单工厂模式(入门) | 一个工厂干所有活:比如 "小作坊",又造圆形又造矩形 | 没有抽象工厂,逻辑都在具体工厂(像责任链只有一个具体处理器) | 产品类型少、变化少(比如只有 3 种形状) |
| 工厂方法模式(进阶) | 一个工厂只干一件活:"圆形工厂" 只造圆形,"矩形工厂" 只造矩形 | 每个具体产品对应一个具体工厂(像责任链每个处理器只处理一种请求) | 产品类型多、需要扩展(比如新增三角形,只加 "三角形工厂") |
| 抽象工厂模式(高阶) | 一个工厂造 "一套产品":比如 "家电工厂" 造电视 + 冰箱,"手机工厂" 造手机 + 耳机 | 抽象工厂定义 "造一套产品" 的接口,具体工厂实现整套生产(像责任链处理 "一系列相关请求") | 产品是 "成套出现" 的(比如数据库驱动 + 方言、UI 组件套系) |
👉 重点:不管哪个变种,核心都没变 ------ 客户端只提需求,工厂负责创建,不用直接new。
七、优点 & 缺点
优点
- 解耦:创建对象和使用对象分开,客户端不用关心 "怎么造",只关心 "怎么用"(像责任链客户端不用关心 "谁处理");
- 易扩展:新增产品时,简单工厂改工厂逻辑,工厂方法 / 抽象工厂只加新工厂 + 新产品,不用改客户端(像责任链加新处理器,不用改客户端);
- 易维护 :创建逻辑集中在工厂,要改只改工厂(比如圆形的
draw()逻辑变了,只改Circle类,不用改所有客户端)。
缺点
- 类变多:工厂方法 / 抽象工厂模式下,一个产品对应一个工厂,类的数量会翻倍
- 简单场景没必要 :如果对象很简单(比如
new User()就一行),用工厂模式会多一个工厂类,反而增加复杂度(像责任链处理简单请求,没必要搞链路); - 扩展有局限:抽象工厂模式如果要新增 "成套产品" 中的一个(比如家电工厂原本造电视 + 冰箱,现在要加洗衣机),需要改抽象工厂接口,违反开闭原则
八、实际应用场景
工厂模式在开发中很常用,到处都是:
- 数据库访问:Hibernate 换数据库时,只改 "数据库工厂" 的配置(MySQL 工厂→Oracle 工厂),不用改业务代码;
- 日志框架:日志可以输出到硬盘、控制台、远程服务器,对应 "硬盘日志工厂""控制台日志工厂",客户端选一个就行;
- 框架源码 :Spring 的
BeanFactory(创建 Bean 对象)、Java 的Calendar.getInstance()(创建日历对象),本质都是工厂模式; - 插件开发:比如 IDE 的插件,每个插件对应一个 "插件工厂",IDE 启动时通过工厂创建插件实例。
九、注意事项
- 别过度设计 :简单对象直接
new,复杂对象(创建要 3 行以上代码)再用工厂; - 优先选简单工厂 / 工厂方法:大多数场景下,简单工厂(产品少)或工厂方法(产品多)就够了,抽象工厂除非是 "成套产品",否则不用;
- 避免工厂逻辑太复杂 :如果工厂里的
if-else太多(比如简单工厂有 10 种产品),可以结合责任链或策略模式优化(但这是进阶操作,基础场景不用)。
总结
工厂模式的核心就是 "把创建对象的活儿交给专业的人(工厂)干",本质是 "创建与使用分离"------ 和责任链 "处理请求与发起请求分离" 的思想异曲同工。
记住:能直接new且不麻烦的,就不用工厂;创建逻辑复杂、需要灵活切换的,就用工厂。按这个原则选,永远不会错!