工厂模式:如何用“标准化生产线”解决对象创建混乱?
你有没有遇到过这样的情况?写电商系统时,创建手机、电脑、平板等商品对象,每个都要new
+一堆参数,后来要加新商品类型,得改遍所有new
的地方——这就是对象创建的“碎片化”问题。工厂模式的核心就是用“标准化生产线”把对象创建逻辑集中起来,让你不用再直接new
,而是“找工厂要”。

1. 简单工厂:快速解决“多类型对象创建”
简单工厂是工厂模式的“入门款”,适合固定类型的对象创建。比如电商系统要创建不同类型的商品,我们可以用简单工厂把“判断类型+创建对象”的逻辑封装起来:
// 商品接口:定义所有商品的通用行为
public interface Product {
void showInfo(); // 展示商品信息
}
// 手机商品实现
public class Phone implements Product {
@Override
public void showInfo() {
System.out.println("商品:智能手机 | 类别:电子设备");
}
}
// 电脑商品实现
public class Computer implements Product {
@Override
public void showInfo() {
System.out.println("商品:笔记本电脑 | 类别:电子设备");
}
}
// 简单工厂类:负责创建商品对象
public class SimpleProductFactory {
// 静态方法:根据类型返回对应商品
public static Product createProduct(String type) {
switch (type.toLowerCase()) {
case "phone":
return new Phone();
case "computer":
return new Computer();
default:
throw new IllegalArgumentException("未知商品类型:" + type);
}
}
}
// 使用示例:不用直接new,找工厂要
public class Client {
public static void main(String[] args) {
Product phone = SimpleProductFactory.createProduct("phone");
phone.showInfo(); // 输出:商品:智能手机 | 类别:电子设备
Product computer = SimpleProductFactory.createProduct("computer");
computer.showInfo(); // 输出:商品:笔记本电脑 | 类别:电子设备
}
}
简单工厂的优点是集中管理创建逻辑,但缺点也明显——如果要加新商品(比如平板),得修改SimpleProductFactory
的switch
语句,违反了“开闭原则”(对扩展开放、对修改关闭)。这时候需要升级到工厂方法模式。
2. 工厂方法:让“新增类型”不用改旧代码
工厂方法模式把“创建具体对象”的责任交给了具体工厂类,每个商品对应一个工厂,这样加新商品时只需加新工厂,不用改旧代码。比如电商要加“平板”商品:
// 工厂接口:定义工厂的通用行为(创建商品)
public interface ProductFactory {
Product createProduct();
}
// 手机工厂:专门创建手机
public class PhoneFactory implements ProductFactory {
@Override
public Product createProduct() {
return new Phone();
}
}
// 电脑工厂:专门创建电脑
public class ComputerFactory implements ProductFactory {
@Override
public Product createProduct() {
return new Computer();
}
}
// 新增平板商品及工厂(无需修改旧代码)
public class Tablet implements Product {
@Override
public void showInfo() {
System.out.println("商品:平板 | 类别:电子设备");
}
}
public class TabletFactory implements ProductFactory {
@Override
public Product createProduct() {
return new Tablet();
}
}
// 使用示例:找具体工厂要商品
public class Client {
public static void main(String[] args) {
// 要手机找手机工厂
ProductFactory phoneFactory = new PhoneFactory();
Product phone = phoneFactory.createProduct();
phone.showInfo();
// 要平板找平板工厂(新增逻辑,不影响旧代码)
ProductFactory tabletFactory = new TabletFactory();
Product tablet = tabletFactory.createProduct();
tablet.showInfo();
}
}
工厂方法完美解决了简单工厂的“开闭原则”问题,但也带来了类数量爆炸的问题——每个商品对应一个工厂,系统复杂时会有很多工厂类。这时候可以用抽象工厂模式解决“产品族”问题(比如商品+配件的组合)。
3. 抽象工厂:解决“产品族”的组合创建
抽象工厂适合一组相关联的对象创建,比如电商系统中,手机要配充电器,电脑要配键盘——这就是“产品族”(商品+配件)。抽象工厂可以一次性创建整个产品族的对象:
// 配件接口:定义配件的通用行为
public interface Accessory {
void use(); // 使用配件
}
// 手机充电器(手机配件)
public class PhoneCharger implements Accessory {
@Override
public void use() {
System.out.println("使用手机充电器:快充50%只需30分钟");
}
}
// 电脑键盘(电脑配件)
public class ComputerKeyboard implements Accessory {
@Override
public void use() {
System.out.println("使用机械键盘:打字手感爽到爆");
}
}
// 抽象工厂接口:定义创建产品族的方法(商品+配件)
public interface ProductFamilyFactory {
Product createProduct(); // 创建商品
Accessory createAccessory(); // 创建对应配件
}
// 手机产品族工厂:创建手机+充电器
public class PhoneFamilyFactory implements ProductFamilyFactory {
@Override
public Product createProduct() {
return new Phone();
}
@Override
public Accessory createAccessory() {
return new PhoneCharger();
}
}
// 电脑产品族工厂:创建电脑+键盘
public class ComputerFamilyFactory implements ProductFamilyFactory {
@Override
public Product createProduct() {
return new Computer();
}
@Override
public Accessory createAccessory() {
return new ComputerKeyboard();
}
}
// 使用示例:一次性获取商品+配件
public class Client {
public static void main(String[] args) {
// 买手机套装:找手机产品族工厂
ProductFamilyFactory phoneSetFactory = new PhoneFamilyFactory();
Product phone = phoneSetFactory.createProduct();
Accessory charger = phoneSetFactory.createAccessory();
phone.showInfo(); // 商品:智能手机...
charger.use(); // 使用手机充电器...
// 买电脑套装:找电脑产品族工厂
ProductFamilyFactory computerSetFactory = new ComputerFamilyFactory();
Product computer = computerSetFactory.createProduct();
Accessory keyboard = computerSetFactory.createAccessory();
computer.showInfo(); // 商品:笔记本电脑...
keyboard.use(); // 使用机械键盘...
}
}
抽象工厂的核心是“族”的概念——如果你需要创建一组相关的对象,用它准没错。
单例模式:如何保证一个类只有“唯一实例”?
单例模式的核心是“一个类在整个系统中只有一个实例”,适合日志工具、配置管理、线程池等场景——你肯定不希望日志工具类被创建多次,导致日志分散到不同文件里吧?
单例模式有5种常见实现,我们逐一拆解它们的优缺点和适用场景:
1. 饿汉模式:最简单的“提前加载”
饿汉模式在类加载时就创建实例,优点是简单、线程安全,但缺点是如果实例不用,会浪费内存:
// 饿汉单例:类加载时初始化实例
public class HungryLogger {
// 私有静态实例(类加载时创建)
private static final HungryLogger INSTANCE = new HungryLogger();
// 私有构造方法:防止外部new
private HungryLogger() {}
// 公有静态方法:返回实例
public static HungryLogger getInstance() {
return INSTANCE;
}
// 日志方法
public void log(String message) {
System.out.println("[饿汉日志] " + message);
}
}
2. 懒汉模式:“用时再加载”但要解决线程安全
懒汉模式在第一次使用时才创建实例(懒加载),但默认线程不安全,需要加synchronized
修饰方法:
// 懒汉单例(线程安全版)
public class LazyLogger {
// 私有静态实例(初始为null)
private static LazyLogger instance;
// 私有构造方法
private LazyLogger() {}
// 公有静态方法:加synchronized保证线程安全
public synchronized static LazyLogger getInstance() {
if (instance == null) {
instance = new LazyLogger();
}
return instance;
}
public void log(String message) {
System.out.println("[懒汉日志] " + message);
}
}
但synchronized
会导致效率低下(每次调用都要锁),这时候可以用双重检查锁(DCL)优化。
3. 双重检查锁(DCL):高效的懒加载
DCL通过“两次检查实例是否为null”+volatile
关键字,解决了线程安全和效率问题:
// 双重检查锁单例
public class DclLogger {
// volatile修饰:防止指令重排(重要!)
private static volatile DclLogger instance;
private DclLogger() {}
public static DclLogger getInstance() {
// 第一次检查:如果实例存在,直接返回(不用锁)
if (instance == null) {
// 加锁:保证同一时间只有一个线程进入
synchronized (DclLogger.class) {
// 第二次检查:防止多个线程等待锁时重复创建
if (instance == null) {
instance = new DclLogger();
}
}
}
return instance;
}
public void log(String message) {
System.out.println("[DCL日志] " + message);
}
}
注意:volatile
关键字不能少——它防止instance = new DclLogger()
被拆分成“分配内存→初始化对象→指向引用”时的指令重排(如果重排,可能会返回未初始化的实例)。
4. 静态内部类:最优雅的懒加载
静态内部类利用类加载机制实现懒加载和线程安全,是《Effective Java》推荐的方式:
// 静态内部类单例
public class InnerClassLogger {
// 私有构造方法
private InnerClassLogger() {}
// 静态内部类:类加载时不会初始化
private static class LoggerHolder {
// 内部类的静态变量:类加载时创建实例
private static final InnerClassLogger INSTANCE = new InnerClassLogger();
}
// 公有静态方法:返回内部类的实例
public static InnerClassLogger getInstance() {
return LoggerHolder.INSTANCE;
}
public void log(String message) {
System.out.println("[静态内部类日志] " + message);
}
}
静态内部类的优点:懒加载、线程安全、无需加锁,缺点是无法传递参数(如果实例需要参数,这种方式不适用)。
5. 枚举单例:最安全的“防反射+防序列化”
枚举单例是《Effective Java》推荐的最佳方式,自动解决线程安全、反射攻击、序列化问题:
// 枚举单例:天然单例
public enum EnumLogger {
INSTANCE; // 唯一实例
// 日志方法
public void log(String message) {
System.out.println("[枚举日志] " + message);
}
}
枚举的优点:
– 线程安全:枚举类的实例在类加载时创建,JVM保证线程安全;
– 防反射:枚举类的构造方法无法被反射调用;
– 防序列化:枚举类的readObject
方法会返回原实例,不会创建新对象。
为了帮你快速选择,我整理了单例模式各实现的对比表:
实现方式 | 懒加载 | 线程安全 | 防反射 | 防序列化 | 适用场景 |
---|---|---|---|---|---|
饿汉模式 | ❌ | ✅ | ❌ | ❌ | 实例占用内存小、常用 |
懒汉模式(同步) | ✅ | ✅ | ❌ | ❌ | 实例不常用、内存大 |
双重检查锁 | ✅ | ✅ | ❌ | ❌ | 需要高效懒加载 |
静态内部类 | ✅ | ✅ | ❌ | ❌ | 无需传参的懒加载 |
枚举模式 | ❌ | ✅ | ✅ | ✅ | 要求绝对安全的场景 |
两种模式的组合使用:解决复杂场景的协同问题
工厂模式和单例模式不是孤立的,它们可以组合使用解决更复杂的问题。比如电商系统中:
– 用工厂模式创建商品对象(标准化生产线);
– 用枚举单例的日志工具类记录商品创建过程(唯一实例,保证日志统一)。
组合使用的代码示例:
// 枚举日志单例(唯一实例)
public enum LogUtil {
INSTANCE;
public void info(String message) {
System.out.println("[INFO] " + message);
}
}
// 商品工厂(同之前的工厂方法模式)
public class PhoneFactory implements ProductFactory {
@Override
public Product createProduct() {
LogUtil.INSTANCE.info("创建手机商品..."); // 记录日志
return new Phone();
}
}
// 使用示例
public class Client {
public static void main(String[] args) {
ProductFactory phoneFactory = new PhoneFactory();
Product phone = phoneFactory.createProduct(); // 会触发日志记录
phone.showInfo();
}
}
运行结果:
[INFO] 创建手机商品...
商品:智能手机 | 类别:电子设备
写在最后:模式不是“银弹”,要按需选择
工厂模式和单例模式都是设计模式中的“基础款”,但不要为了用模式而用模式——比如:
– 如果对象类型固定、简单,用简单工厂就够了,不用强行上抽象工厂;
– 如果实例不需要懒加载,用饿汉模式比DCL更简单。
关键是解决实际问题:工厂模式帮你解决“对象创建混乱”,单例模式帮你解决“唯一实例保证”。理解它们的原理,结合场景选择实现方式,才能真正发挥设计模式的价值。
原创文章,作者:,如若转载,请注明出处:https://zube.cn/archives/299