Java 中的设计模式:单例模式与工厂模式解析

举报
江南清风起 发表于 2025/03/12 11:42:39 2025/03/12
【摘要】 Java 中的设计模式:单例模式与工厂模式解析设计模式是软件开发中总结出的经典解决方案,它们能够提升代码的可读性、可维护性和可复用性。本文将深入探讨 Java 中的 单例模式(Singleton Pattern) 和 工厂模式(Factory Pattern),通过详细的代码实例和解析,帮助你更好地理解这两种常见的设计模式。 1. 单例模式(Singleton Pattern) 1.1 单...

Java 中的设计模式:单例模式与工厂模式解析

设计模式是软件开发中总结出的经典解决方案,它们能够提升代码的可读性、可维护性和可复用性。本文将深入探讨 Java 中的 单例模式(Singleton Pattern)工厂模式(Factory Pattern),通过详细的代码实例和解析,帮助你更好地理解这两种常见的设计模式。


1. 单例模式(Singleton Pattern)

1.1 单例模式简介

单例模式确保一个类在整个应用程序生命周期内仅有一个实例,并提供一个全局访问点。常见的应用场景包括:

  • 数据库连接管理
  • 线程池
  • 配置管理
  • 日志管理

1.2 经典实现方式

1.2.1 饿汉式(线程安全,类加载时初始化)

public class SingletonEager {
    // 类加载时就创建实例
    private static final SingletonEager INSTANCE = new SingletonEager();

    // 私有构造方法,防止外部实例化
    private SingletonEager() {}

    // 提供公共访问方法
    public static SingletonEager getInstance() {
        return INSTANCE;
    }
}

优点: 线程安全,简单易实现
缺点: 类加载时就创建实例,可能会浪费内存


1.2.2 懒汉式(线程不安全,按需初始化)

public class SingletonLazy {
    private static SingletonLazy instance;

    private SingletonLazy() {}

    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

优点: 按需初始化,节省内存
缺点: 线程不安全,可能导致多个实例的创建


1.2.3 线程安全的懒汉式(同步方法)

public class SingletonSynchronized {
    private static SingletonSynchronized instance;

    private SingletonSynchronized() {}

    public static synchronized SingletonSynchronized getInstance() {
        if (instance == null) {
            instance = new SingletonSynchronized();
        }
        return instance;
    }
}

优点: 线程安全
缺点: 每次获取实例都需要同步,影响性能


1.2.4 双重检查锁(推荐使用)

public class SingletonDoubleChecked {
    private static volatile SingletonDoubleChecked instance;

    private SingletonDoubleChecked() {}

    public static SingletonDoubleChecked getInstance() {
        if (instance == null) {
            synchronized (SingletonDoubleChecked.class) {
                if (instance == null) {
                    instance = new SingletonDoubleChecked();
                }
            }
        }
        return instance;
    }
}

优点: 线程安全,性能较高
缺点: 需要 volatile 关键字,确保可见性


1.2.5 静态内部类(推荐)

public class SingletonHolder {
    private SingletonHolder() {}

    private static class Holder {
        private static final SingletonHolder INSTANCE = new SingletonHolder();
    }

    public static SingletonHolder getInstance() {
        return Holder.INSTANCE;
    }
}

优点: 线程安全,懒加载,推荐使用
缺点: 依赖类加载机制,可能在某些场景下不适用


2. 工厂模式(Factory Pattern)

2.1 工厂模式简介

工厂模式是一种创建型设计模式,它通过封装对象创建逻辑,避免在代码中直接使用 new 关键字,提高代码的灵活性和可维护性。

常见的工厂模式包括:

  • 简单工厂模式(Simple Factory)
  • 工厂方法模式(Factory Method)
  • 抽象工厂模式(Abstract Factory)

2.2 简单工厂模式(静态工厂模式)

适用于创建类型较少、变化不大的对象。

// 产品接口
interface Product {
    void use();
}

// 具体产品A
class ProductA implements Product {
    @Override
    public void use() {
        System.out.println("使用产品A");
    }
}

// 具体产品B
class ProductB implements Product {
    @Override
    public void use() {
        System.out.println("使用产品B");
    }
}

// 简单工厂类
class SimpleFactory {
    public static Product createProduct(String type) {
        switch (type) {
            case "A": return new ProductA();
            case "B": return new ProductB();
            default: throw new IllegalArgumentException("未知产品类型");
        }
    }
}

// 测试
public class SimpleFactoryTest {
    public static void main(String[] args) {
        Product product = SimpleFactory.createProduct("A");
        product.use();
    }
}

优点: 简单易用,封装对象创建
缺点: 不符合开闭原则,新增产品需修改工厂代码


2.3 工厂方法模式(面向接口)

适用于扩展性强的场景,每个产品有独立的工厂。

// 产品接口
interface Product {
    void use();
}

// 具体产品A
class ProductA implements Product {
    @Override
    public void use() {
        System.out.println("使用产品A");
    }
}

// 具体产品B
class ProductB implements Product {
    @Override
    public void use() {
        System.out.println("使用产品B");
    }
}

// 工厂接口
interface Factory {
    Product createProduct();
}

// 具体工厂A
class FactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ProductA();
    }
}

// 具体工厂B
class FactoryB implements Factory {
    @Override
    public Product createProduct() {
        return new ProductB();
    }
}

// 测试
public class FactoryMethodTest {
    public static void main(String[] args) {
        Factory factory = new FactoryA();
        Product product = factory.createProduct();
        product.use();
    }
}

优点: 遵循开闭原则,新产品只需新增工厂
缺点: 增加系统复杂性,每个产品需创建一个工厂类


2.4 抽象工厂模式(多种产品族)

适用于需要创建一系列相关产品的场景。

// 抽象产品接口
interface CPU {
    void compute();
}

// 具体产品
class IntelCPU implements CPU {
    @Override
    public void compute() {
        System.out.println("Intel CPU 计算");
    }
}

class AMDCPU implements CPU {
    @Override
    public void compute() {
        System.out.println("AMD CPU 计算");
    }
}

// 抽象工厂
interface CPUFactory {
    CPU createCPU();
}

// 具体工厂
class IntelFactory implements CPUFactory {
    @Override
    public CPU createCPU() {
        return new IntelCPU();
    }
}

class AMDFactory implements CPUFactory {
    @Override
    public CPU createCPU() {
        return new AMDCPU();
    }
}

// 测试
public class AbstractFactoryTest {
    public static void main(String[] args) {
        CPUFactory factory = new IntelFactory();
        CPU cpu = factory.createCPU();
        cpu.compute();
    }
}

优点: 支持多产品族创建,遵循开闭原则
缺点: 结构复杂,适用于大规模系统

3. 单例模式与工厂模式的结合

在实际开发中,单例模式和工厂模式并不是孤立存在的,而是可以结合使用的。例如,我们可以使用工厂模式来管理单例对象的创建,从而增强代码的灵活性。

3.1 结合单例模式的工厂模式

在某些情况下,我们希望某个对象在全局范围内只存在一个实例(单例模式),但仍然希望通过工厂模式来管理对象的创建。这可以通过 工厂方法模式 + 单例模式 组合实现。

3.1.1 示例:单例模式的数据库连接工厂

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

// 数据库连接管理器(单例模式)
class DatabaseConnection {
    private static volatile DatabaseConnection instance;
    private Connection connection;

    private DatabaseConnection() {
        try {
            // 这里使用 SQLite,实际项目可改为 MySQL、PostgreSQL 等
            String url = "jdbc:sqlite:sample.db";
            connection = DriverManager.getConnection(url);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static DatabaseConnection getInstance() {
        if (instance == null) {
            synchronized (DatabaseConnection.class) {
                if (instance == null) {
                    instance = new DatabaseConnection();
                }
            }
        }
        return instance;
    }

    public Connection getConnection() {
        return connection;
    }
}

// 工厂类
class ConnectionFactory {
    public static Connection getConnection() {
        return DatabaseConnection.getInstance().getConnection();
    }
}

// 测试
public class SingletonFactoryTest {
    public static void main(String[] args) {
        Connection conn1 = ConnectionFactory.getConnection();
        Connection conn2 = ConnectionFactory.getConnection();

        System.out.println(conn1 == conn2); // true,证明是同一个实例
    }
}

3.2 结合工厂模式的单例模式

有时候,我们的单例类内部可能需要管理多个子类实例,这种情况下,工厂模式可以帮助我们 动态创建和管理不同的实例

3.2.1 示例:日志管理器

// 日志接口
interface Logger {
    void log(String message);
}

// 具体日志实现:控制台日志
class ConsoleLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("ConsoleLogger: " + message);
    }
}

// 具体日志实现:文件日志
class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("FileLogger: " + message);
        // 实际开发中,这里应写入文件
    }
}

// 日志工厂(结合单例模式)
class LoggerFactory {
    private static final LoggerFactory instance = new LoggerFactory();
    private Logger logger;

    private LoggerFactory() {}

    public static LoggerFactory getInstance() {
        return instance;
    }

    public Logger getLogger(String type) {
        if (logger == null) {
            switch (type) {
                case "console":
                    logger = new ConsoleLogger();
                    break;
                case "file":
                    logger = new FileLogger();
                    break;
                default:
                    throw new IllegalArgumentException("未知的日志类型");
            }
        }
        return logger;
    }
}

// 测试
public class SingletonFactoryLoggerTest {
    public static void main(String[] args) {
        Logger logger1 = LoggerFactory.getInstance().getLogger("console");
        Logger logger2 = LoggerFactory.getInstance().getLogger("console");

        logger1.log("Hello, Singleton Factory!");

        System.out.println(logger1 == logger2); // true,证明是同一个实例
    }
}

4. 线程安全问题与性能优化

4.1 单例模式的线程安全问题

在高并发环境下,懒汉式单例 存在线程安全问题,例如:

public class UnsafeSingleton {
    private static UnsafeSingleton instance;

    private UnsafeSingleton() {}

    public static UnsafeSingleton getInstance() {
        if (instance == null) { 
            instance = new UnsafeSingleton(); // 可能多个线程同时进入此处
        }
        return instance;
    }
}

改进方案:

  1. 同步方法(但性能低)
  2. 双重检查锁
  3. 静态内部类

4.2 工厂模式的线程安全问题

如果工厂类是无状态的(如 SimpleFactory),则 通常不需要 额外的同步控制。但如果工厂类涉及到 对象的缓存或复用,就需要考虑线程安全问题。

例如,在工厂中维护一个 对象缓存池

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

// 线程安全的工厂
class ThreadSafeFactory {
    private static final Map<String, Product> productCache = new ConcurrentHashMap<>();

    public static Product getProduct(String type) {
        return productCache.computeIfAbsent(type, k -> {
            switch (k) {
                case "A": return new ProductA();
                case "B": return new ProductB();
                default: throw new IllegalArgumentException("未知产品类型");
            }
        });
    }
}

这样可以确保工厂不会重复创建相同的对象,同时 ConcurrentHashMap 保证了线程安全。


5. 单例模式与工厂模式的适用场景

5.1 何时使用单例模式?

  • 全局共享的资源(如数据库连接池、线程池、配置管理)
  • 需要确保唯一实例的对象(如 Runtime.getRuntime()

5.2 何时使用工厂模式?

  • 创建逻辑复杂 的对象(如 ConnectionThread
  • 需要解耦对象创建 和使用(如 Spring BeanFactory
  • 希望增强代码的扩展性(如新增 ProductC 时无需修改已有代码)

5.3 何时结合使用?

  • 既想确保对象唯一性(单例模式),又想提供灵活的创建方式(工厂模式)
  • 例如:数据库连接池、日志管理器、线程池

6. 进一步扩展:结合反射与枚举

6.1 反射破坏单例模式

即使我们使用 私有构造方法,反射仍然可以破坏单例模式:

import java.lang.reflect.Constructor;

public class ReflectionAttack {
    public static void main(String[] args) throws Exception {
        SingletonHolder instance1 = SingletonHolder.getInstance();

        Constructor<SingletonHolder> constructor = SingletonHolder.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        SingletonHolder instance2 = constructor.newInstance();

        System.out.println(instance1 == instance2); // false,实例被破坏!
    }
}

解决方案: 在构造方法中防止重复创建:

private SingletonHolder() {
    if (Holder.INSTANCE != null) {
        throw new RuntimeException("禁止反射创建实例");
    }
}

6.2 使用枚举实现单例模式(最安全)

public enum EnumSingleton {
    INSTANCE;

    public void doSomething() {
        System.out.println("枚举单例");
    }
}

优点:

  • 天然防止反射攻击
  • 天然线程安全
  • 易于序列化

至此,我们深入探讨了 Java 设计模式中的单例模式与工厂模式,并结合代码示例演示了多种应用场景。你是否有更好的实现方式?欢迎讨论!🚀

image.png

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。