工厂模式与单例模式实战:从原理到代码落地指南

工厂模式:如何用“标准化生产线”解决对象创建混乱?

你有没有遇到过这样的情况?写电商系统时,创建手机、电脑、平板等商品对象,每个都要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(); // 输出:商品:笔记本电脑 | 类别:电子设备
    }
}

简单工厂的优点是集中管理创建逻辑,但缺点也明显——如果要加新商品(比如平板),得修改SimpleProductFactoryswitch语句,违反了“开闭原则”(对扩展开放、对修改关闭)。这时候需要升级到工厂方法模式

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

(0)