什么是工厂模式?3种实现方式的区别和特点?
面试考察点
-
演进思维:面试官不仅仅是想知道三种工厂模式分别是什么,更是想看你能否讲清楚它们的演进逻辑——简单工厂有什么问题?工厂方法为什么出现?抽象工厂又解决了什么问题?这条线理顺了,说明你是真懂了。
-
开闭原则理解:三种工厂模式对开闭原则的满足程度不同,面试官想看你是否理解这个递进关系。
-
实战关联:能否把工厂模式和 Spring 的
BeanFactory、FactoryBean、日志门面SLF4J等框架联系起来。
核心答案
一句话定义:工厂模式将对象的创建过程封装起来,客户端不需要知道具体创建了哪个类,只需要通过工厂获取对象即可。
工厂模式有 3 种实现方式,它们是一个逐步演进的关系:
| 类型 | 核心思想 | 工厂类数量 | 适用场景 |
|---|---|---|---|
| 简单工厂 | 一个工厂类,通过参数决定创建哪个产品 | 1 个 | 产品种类少且不常扩展 |
| 工厂方法 | 每个产品对应一个工厂类 | 多个 | 产品种类多、需要扩展 |
| 抽象工厂 | 一个工厂创建一族相关产品 | 多个 | 需要创建产品族 |
上面的图展示了三种工厂模式的演进逻辑:
- 简单工厂:所有产品集中在一个工厂里创建,通过参数区分。简单直接,但扩展性差。
- 工厂方法:把工厂拆开,每个产品对应一个工厂。扩展性好,但类会变多。
- 抽象工厂:在工厂方法的基础上,让一个工厂能创建一族相关的产品。解决 "产品族" 问题。
深度解析
一、简单工厂模式(静态工厂方法)
先定义产品接口和具体产品:
// 产品接口
public interface Phone {
void produce();
}
// 具体产品 A
public class ApplePhone implements Phone {
@Override
public void produce() {
System.out.println("生产苹果手机");
}
}
// 具体产品 B
public class HuaweiPhone implements Phone {
@Override
public void produce() {
System.out.println("生产华为手机");
}
}
工厂类:
public class PhoneFactory {
// 静态方法,根据参数创建不同的产品
public static Phone createPhone(String type) {
if ("apple".equals(type)) {
return new ApplePhone();
} else if ("huawei".equals(type)) {
return new HuaweiPhone();
}
throw new IllegalArgumentException("未知手机类型:" + type);
}
}
// 使用
Phone phone = PhoneFactory.createPhone("apple");
phone.produce(); // 生产苹果手机
简单工厂的优点很明显——客户端只需要传个参数就行,不用关心具体创建逻辑。缺点也很致命:每新增一个产品,都要改 createPhone() 方法,加一个 else if。违反了开闭原则。
简单工厂其实不算 GoF 23 种设计模式里的,它更像是一种编程习惯。但面试经常把它和另外两种放在一起问,因为它确实是工厂方法的 "前身"。
二、工厂方法模式
工厂方法的核心改进:把工厂类也抽象化,每个具体产品对应一个具体工厂。
// 抽象工厂
public interface PhoneFactory {
Phone createPhone();
}
// 苹果工厂——只生产苹果手机
public class ApplePhoneFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new ApplePhone();
}
}
// 华为工厂——只生产华为手机
public class HuaweiPhoneFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new HuaweiPhone();
}
}
// 使用
PhoneFactory factory = new ApplePhoneFactory();
Phone phone = factory.createPhone();
phone.produce(); // 生产苹果手机
好处是什么?新增产品完全不用改已有代码。比如要加一个小米手机:
// 新增产品
public class XiaomiPhone implements Phone {
@Override
public void produce() {
System.out.println("生产小米手机");
}
}
// 新增工厂
public class XiaomiPhoneFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new XiaomiPhone();
}
}
ApplePhoneFactory、HuaweiPhoneFactory 一行都不用改,完美符合开闭原则。
代价就是类的数量增多了——一个产品对应一个工厂。但这在面向对象设计里是完全可以接受的,用类的数量换可维护性。
三、抽象工厂模式
抽象工厂解决的是一个更复杂的问题——产品族。
什么叫产品族?打个比方:苹果有手机和耳机,华为也有手机和耳机。苹果的手机和耳机是一族(风格统一),华为的也是一族。你不会拿苹果手机配华为耳机(虽然物理上可以),因为产品族不一致。
// 产品族接口:手机
public interface Phone {
void call();
}
// 产品族接口:耳机
public interface Earphone {
void listen();
}
// 抽象工厂:定义创建一族产品的接口
public interface ElectronicsFactory {
Phone createPhone();
Earphone createEarphone();
}
苹果产品族:
public class ApplePhone implements Phone {
@Override
public void call() {
System.out.println("用 iPhone 打电话");
}
}
public class AirPods implements Earphone {
@Override
public void listen() {
System.out.println("用 AirPods 听歌");
}
}
// 苹果工厂:创建一族的苹果产品
public class AppleFactory implements ElectronicsFactory {
@Override
public Phone createPhone() {
return new ApplePhone();
}
@Override
public Earphone createEarphone() {
return new AirPods();
}
}
华为产品族:
public class HuaweiPhone implements Phone {
@Override
public void call() {
System.out.println("用华为 Mate 打电话");
}
}
public class FreeBuds implements Earphone {
@Override
public void listen() {
System.out.println("用 FreeBuds 听歌");
}
}
// 华为工厂:创建一族的华为产品
public class HuaweiFactory implements ElectronicsFactory {
@Override
public Phone createPhone() {
return new HuaweiPhone();
}
@Override
public Earphone createEarphone() {
return new FreeBuds();
}
}
使用:
// 切换整个产品族,只需要换一个工厂
ElectronicsFactory factory = new AppleFactory();
factory.createPhone().call(); // 用 iPhone 打电话
factory.createEarphone().listen(); // 用 AirPods 听歌
// 想换华为?换一个工厂就行
factory = new HuaweiFactory();
factory.createPhone().call(); // 用华为 Mate 打电话
factory.createEarphone().listen(); // 用 FreeBuds 听歌
抽象工厂最大的优势就是保证产品族的一致性——你拿到 AppleFactory,创建出来的手机和耳机一定是苹果的,不会出现混搭。
但抽象工厂也有一个缺点:新增产品等级(比如新增一个 "智能手表" 产品)需要修改所有的工厂类。这点和简单工厂的问题类似。所以抽象工厂适合 "产品等级结构稳定,但产品族经常变化" 的场景。
四、三种模式对比总结
| 对比项 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 工厂类数量 | 1 个 | 多个(每产品一个) | 多个(每产品族一个) |
| 产品维度 | 单一产品 | 单一产品 | 产品族(多等级) |
| 新增产品 | ❌ 改工厂类 | ✅ 新增工厂类 | ❌ 改所有工厂类 |
| 新增产品族 | — | — | ✅ 新增工厂类 |
| 开闭原则 | 不满足 | 部分满足 | 部分满足 |
| 复杂度 | 低 | 中 | 高 |
五、框架中的工厂模式
1. Spring 的 BeanFactory
BeanFactory 是 Spring 的核心容器,本质上就是一个大工厂。你通过 getBean() 获取对象,不需要知道 Bean 是怎么创建的——可能是反射实例化的,可能是通过工厂方法创建的,可能是从配置类里生成的。
BeanFactory factory = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = factory.getBean(UserService.class);
2. Spring 的 FactoryBean
FactoryBean 是 Spring 提供的一个工厂接口,让你可以自定义复杂对象的创建逻辑。MyBatis 的 SqlSessionFactoryBean 就是一个典型应用:
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory> {
@Override
public SqlSessionFactory getObject() {
// 复杂的创建逻辑...
return sqlSessionFactory;
}
}
3. SLF4J 日志门面
LoggerFactory.getLogger() 本质上就是一个简单工厂。你不需要关心底层用的是 Logback 还是 Log4j2,工厂帮你搞定。
4. JDBC DriverManager.getConnection()
根据 URL 创建不同数据库的连接,也是简单工厂的思想。
面试高频追问
-
简单工厂、工厂方法、抽象工厂有什么区别?
- 简单工厂:一个工厂通过参数创建不同产品,新增产品要改工厂。工厂方法:每个产品一个工厂,新增产品只需新增工厂。抽象工厂:一个工厂创建一族产品,新增产品族只需新增工厂,但新增产品等级要改所有工厂。
-
抽象工厂的优缺点是什么?
- 优点:保证产品族一致性,切换产品族只需换一个工厂。缺点:新增产品等级结构需要修改所有工厂类,违反开闭原则。
-
Spring 中哪里用到了工厂模式?
BeanFactory(IoC 容器本身就是工厂)、FactoryBean(自定义复杂对象创建)、ApplicationContext(Bean 的大工厂)。
常见面试变体
- "手写一个工厂模式"
- "工厂方法模式和抽象工厂模式的区别?"
- "Spring 的
BeanFactory和FactoryBean有什么区别?" - "开闭原则在工厂模式中是怎么体现的?"
记忆口诀
三种工厂:简单(一个工厂搞定)、方法(一个产品一个工厂)、抽象(一族产品一个工厂)。
演进逻辑:简单工厂违反开闭 → 工厂方法拆工厂 → 抽象工厂管产品族。
选择依据:产品少用简单,产品多用方法,产品族用抽象。
总结
工厂模式的演进逻辑就是一部 "消灭 if-else、追求开闭原则" 的历史。面试时先说三种工厂的核心区别,再用 "手机生产" 的例子依次展开,最后结合 Spring 的 BeanFactory 和 FactoryBean 讲框架应用。能把演进逻辑讲清楚,面试官就知道你是真理解了,而不是背了三种写法。
