工厂模式、代理模式与单例模式的介绍

前言

设计模式是软件开发中经过验证的解决方案,它们帮助我们解决常见的软件设计问题。本文将详细介绍三种最常用且重要的设计模式:单例模式(Singleton Pattern)工厂模式(Factory Pattern)代理模式(Proxy Pattern)

一、单例模式(Singleton Pattern)

1.1 模式概述

单例模式是创建型设计模式,其核心思想是确保一个类只有一个实例,并提供一个全局访问点。这种模式在需要控制资源访问、避免重复创建对象等场景中非常有用。

核心特点

  • 唯一性:一个类只能创建一个实例对象
  • 全局访问:通过静态方法获取实例,整个应用程序共享该实例
  • 延迟加载(可选):在需要时才创建实例,节省资源

应用场景:数据库连接池、日志系统、配置管理器、线程池等。

1.2 饿汉式实现

复制代码
public class HungrySingleton {
    // 类加载时就创建实例,线程安全
    private static final HungrySingleton instance = new HungrySingleton();

    // 私有构造函数,防止外部实例化
    private HungrySingleton() {
    }

    // 提供全局访问点
    public static HungrySingleton getInstance() {
        return instance ;
    }

    public void showMessage() {
        System.out.println("Hello from HungrySingleton!");
    }
}

代码解释

  • 类加载时立即创建instance实例,使用final关键字确保引用不可变
  • 私有构造函数,防止通过new关键字创建实例
  • 提供静态方法getInstance获取唯一实例

优点:实现简单,线程安全

缺点:类加载时就创建实例,即使不使用也会占用资源,可能造成资源浪费

1.3 懒汉式实现

复制代码
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {
    }

    // 同步方法获取实例,保证线程安全
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

代码解释

  • synchronized关键字修饰后保证在多线程环境下只创建一个实例
  • 加锁和if判断双重检查,只有实例为空时才创建
  • 实现了延迟加载,节省资源

缺点:每次调用getInstance都需要加锁,性能开销较大

1.4 双重检查锁定实现

复制代码
public class DoubleCheckedSingleton {
    // volatile确保可见性和禁止指令重排序
    private static volatile DoubleCheckedSingleton instance;

    private DoubleCheckedSingleton() {
    }

    public static DoubleCheckedSingleton getInstance() {
        // 第一次检查:避免不必要的同步
        if (instance == null) {
            synchronized (DoubleCheckedSingleton.class) {
                // 第二次检查:确保在多线程环境下只创建一个实例
                if (instance == null) {
                    instance = new DoubleCheckedSingleton();
                }
            }
        }
        return instance;
    }
}

代码解释:

  • 用volatile修饰,确保多线程环境下instance变量的可见性,防止指令重排序
  • 第一次进行if判断是为了减少不必要的加锁,提升性能
  • 同步代码块内的第二次检查, 确保在多线程环境下只创建一个实例

优点:延迟加载、线程安全、性能优秀,是推荐的实现方式

1.5 静态内部类实现

复制代码
public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {
    }

    // 静态内部类,只有在调用getInstance()时才会被加载
    private static class SingletonHolder {
        private static final StaticInnerClassSingleton instance =
            new StaticInnerClassSingleton();
    }

    public static StaticInnerClassSingleton getInstance() {
        return SingletonHolder.instance ;
    }
}

代码解释

  • 创建一个静态内部类,只有在调用getInstance()时才会被加载
  • 静态内部类的实例创建发生在被加载时,且由JVM保证线程安全
  • 这种方式既实现了延迟加载,又保证了线程安全,是最佳实现方式

优点:延迟加载、线程安全、性能优秀,是推荐的实现方式

1.6 单例模式总结

实现方式 延迟加载 线程安全 性能 推荐程度
饿汉式 一般
懒汉式 不推荐
双重检查锁定 推荐
静态内部类 推荐

二、工厂模式(Factory Pattern)

2.1 模式概述

工厂模式是创建型设计模式,其核心思想是将对象的创建与使用分离,通过工厂类来创建对象。这种模式降低了客户端代码与具体产品类之间的耦合度,提高了系统的可维护性和可扩展性。

模式分类

  • 简单工厂(Simple Factory):一个工厂类根据参数创建不同的产品
  • 工厂方法(Factory Method):定义一个创建对象的接口,由子类决定实例化哪个类
  • 抽象工厂(Abstract Factory):提供一个创建一系列相关对象的接口

2.2 简单工厂模式

复制代码
// 产品接口
public interface Product {
    void operation();
}

// 具体产品A
public class ConcreteProductA implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductA operation");
    }
}

// 具体产品B
public class ConcreteProductB implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductB operation");
    }
}

// 简单工厂
public class SimpleFactory {
    public Product createProduct(String type) {
        if ("A".equals(type)) {
            return new ConcreteProductA();
        } else if ("B".equals(type)) {
            return new ConcreteProductB();
        } else {
            throw new IllegalArgumentException("Unknown product type: " + type);
        }
    }
}

// 客户端使用
public class SimpleFactoryClient {
    public static void main(String[] args) {
        SimpleFactory factory = new SimpleFactory();
        Product productA = factory.createProduct("A");
        productA.operation();
    }
}

代码解释

  • product接口:定义产品的通用行为,所有产品都实现这个接口
  • simpleFactory是一个简单工厂类,根据参数创建相应的产品对象
  • 客户端代码通过工厂类获取产品,无需知道具体的产品类名

优点:封装了对象创建过程,客户端代码与具体产品类解耦

缺点:违反开闭原则,增加新产品需要修改工厂类的d

2.3 工厂方法模式

复制代码
// 产品接口
public interface Product {
    void operation();
}

// 具体产品
public class ConcreteProductA implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductA operation");
    }
}

public class ConcreteProductB implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductB operation");
    }
}

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

// 具体工厂A - 创建产品A
public class ConcreteFactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}

// 具体工厂B - 创建产品B
public class ConcreteFactoryB implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}

// 客户端使用
public class FactoryMethodClient {
    public static void main(String[] args) {
        Factory factoryA = new ConcreteFactoryA();
        Product productA = factoryA.createProduct();
        productA.operation();
    }
}

代码解释

  • Factory接口用来定义创建产品的抽象方法
  • ConcreteProductA和 ConcreteProductB都是具体工厂类,负责创建具体产品
  • 每个工厂只负责创建一种产品,符合单一职责原则
  • 客户端依赖抽象工厂接口和抽象产品接口,添加的新产品类只需要实现Factory接口即可

优点:符合开闭原则,新增产品只需添加新的工厂类

2.4 抽象工厂模式

复制代码
// 产品族接口A
public interface ProductA {
    void operationA();
}

// 产品族接口B
public interface ProductB {
    void operationB();
}

// 产品实现
public class ProductA1 implements ProductA {
    @Override
    public void operationA() {
        System.out.println("ProductA1 operationA");
    }
}

public class ProductB1 implements ProductB {
    @Override
    public void operationB() {
        System.out.println("ProductB1 operationB");
    }
}

// 抽象工厂接口 - 可创建多个产品族
public interface AbstractFactory {
    ProductA createProductA();
    ProductB createProductB();
}

// 具体工厂1 - 创建产品族1
public class ConcreteFactory1 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ProductA1();
    }

    @Override
    public ProductB createProductB() {
        return new ProductB1();
    }
}

// 客户端使用
public class AbstractFactoryClient {
    public static void main(String[] args) {
        AbstractFactory factory1 = new ConcreteFactory1();
        ProductA productA1 = factory1.createProductA();
        ProductB productB1 = factory1.createProductB();
        productA1.operationA();
        productB1.operationB();
    }
}

代码解释

  • 抽象工厂模式用于创建产品族(一系列相关的产品)
  • AbstractFactory接口定义创建多个产品的方法
  • 客户端代码可以切换整个产品族,而不需要修改其他代码

优点:可以创建一系列相关的产品对象,符合开闭原则

2.5 工厂模式对比

特性 简单工厂 工厂方法 抽象工厂
产品复杂度 单一产品 单一产品族 多产品族
开闭原则 违反 符合 部分符合
代码复杂度
适用场景 产品类型少 产品种类多 产品族多

三、代理模式(Proxy Pattern)

3.1 模式概述

代理模式是结构型设计模式,其核心思想是为另一个对象提供一个替身或占位符,以控制对这个对象的访问。代理对象可以在不改变目标对象的情况下,增加额外的功能操作。

模式角色

  • Subject(真实对象的抽象):定义目标对象和代理对象的公共接口
  • RealSubject(真实对象):定义代理对象所代表的真实实体
  • Proxy(代理):保存一个引用使得代理可以访问实体,并提供与主题相同的接口

应用场景:远程代理、虚拟代理、安全代理、智能引用等。

3.2 静态代理

复制代码
// 真实对象的抽象接口
public interface Subject {
    void request();
}

// 真实对象 - 实现实际的业务逻辑
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: 处理实际请求");
    }
}

// 代理类 - 在调用真实对象前后进行增强
public class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject() {
        this.realSubject = new RealSubject();
    }

    @Override
    public void request() {
        // 调用真实对象之前的操作
        System.out.println("ProxySubject: 调用前的准备工作");

        // 调用真实对象的方法
        realSubject.request();

        // 调用真实对象之后的操作
        System.out.println("ProxySubject: 调用后的清理工作");
    }
}

// 客户端使用
public class StaticProxyClient {
    public static void main(String[] args) {
        Subject subject = new ProxySubject();
        subject.request();
    }
}

代码解释

  • Subject接口:定义代理和真实对象的公共接口
  • RealSubject:实现实际业务逻辑的真实主题
  • ProxySubject:代理类,在调用真实主题方法前后添加额外操作

优点:可以在不修改真实主题的情况下增加功能

缺点:需要为每个真实主题创建代理类,代码量大时难以维护

3.3 JDK动态代理

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 真实对象的抽象接口
public interface Subject {
    void request();
    String getData();
}

// 真实对象
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: 处理实际请求");
    }

    @Override
    public String getData() {
        return "RealSubject: 数据";
    }
}

// InvocationHandler实现类 - 包含增强逻辑
public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 调用前的增强逻辑
        System.out.println("方法 " + method.getName() + " 调用前");

        // 调用目标对象的方法
        Object result = method.invoke(target, args);

        // 调用后的增强逻辑
        System.out.println("方法 " + method.getName() + " 调用后");

        return result;
    }
}

// JDK动态代理客户端
public class DynamicProxyClient {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        MyInvocationHandler handler = new MyInvocationHandler(realSubject);

        // 创建代理对象
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
            RealSubject.class.getClassLoader(),
            RealSubject.class.getInterfaces(),
            handler
        );

        proxySubject.request();
        System.out.println("获取到的数据: " + proxySubject.getData());
    }
}

代码解释

  • InvocationHandler接口:动态代理的核心,包含了增强逻辑
  • invoke方法:当调用代理对象的方法时,会自动调用这个方法
  • Proxy.newProxyInstance:在运行时动态创建代理对象
  • 代理对象实现了目标对象的所有接口

优点:可以在运行时动态创建代理,无需为每个类编写代理类

缺点:只能代理接口,不能代理具体类

3.4 代理模式应用实例

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

// 计算接口
interface Calculator {
    int add(int a, int b);
    int subtract(int a, int b);
}

// 计算器实现
class CalculatorImpl implements Calculator {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public int subtract(int a, int b) {
        return a - b;
    }
}

// 计算器代理(带日志和性能监控)
class CalculatorProxy implements InvocationHandler {
    private Calculator target;

    public CalculatorProxy(Calculator target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startTime = System.currentTimeMillis();

        // 日志
        System.out.println("方法 " + method.getName() + " 开始执行,参数: " +
                          Arrays.toString(args));

        // 调用实际方法
        Object result = method.invoke(target, args);

        long endTime = System.currentTimeMillis();
        System.out.println("方法 " + method.getName() + " 执行完成,结果: " + result);
        System.out.println("执行耗时: " + (endTime - startTime) + "ms");

        return result;
    }
}

// 代理模式应用测试
public class ProxyPatternDemo {
    public static void main(String[] args) {
        CalculatorImpl calculator = new CalculatorImpl();
        InvocationHandler handler = new CalculatorProxy(calculator);

        Calculator proxyCalculator = (Calculator) Proxy.newProxyInstance(
            CalculatorImpl.class.getClassLoader(),
            CalculatorImpl.class.getInterfaces(),
            handler
        );

        // 使用代理计算器
        int result1 = proxyCalculator.add(10, 5);
        System.out.println("10 + 5 = " + result1);

        int result2 = proxyCalculator.subtract(10, 5);
        System.out.println("10 - 5 = " + result2);
    }
}

运行结果

复制代码
方法 add 开始执行,参数: [10, 5]
方法 add 执行完成,结果: 15
执行耗时: 0ms
10 + 5 = 15
方法 subtract 开始执行,参数: [10, 5]
方法 subtract 执行完成,结果: 5
执行耗时: 0ms
10 - 5 = 5

代码解释

  • CalculatorProxy为Calculator添加了日志和性能监控功能
  • 客户端使用代理对象调用方法,无需修改原有代码
  • 代理模式实现了业务逻辑和增强逻辑的分离

四、模式对比与总结

4.1 三种模式对比

模式 类型 核心作用 优点 缺点
单例模式 创建型 确保唯一实例 控制资源、全局访问 可能过度使用
工厂模式 创建型 封装对象创建 解耦、符合开闭原则 增加类数量
代理模式 结构型 控制对象访问 增强功能、安全控制 增加调用复杂度

4.2 使用建议

单例模式:用于确实只需要一个实例的场景,推荐使用双重检查锁定或静态内部类实现。

工厂模式:当对象创建逻辑复杂时使用,根据复杂度选择简单工厂、工厂方法或抽象工厂,优先考虑工厂方法模式。

代理模式:需要在访问对象时添加额外功能时使用,优先使用JDK动态代理,无法代理类时使用CGLIB。

4.3 模式组合使用

在实际开发中,这三种模式经常组合使用:

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

// 代理工厂(单例 + 工厂 + 代理)
public class ProxyFactory {
    // 单例
    private static ProxyFactory instance;

    private ProxyFactory() {
    }

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

    // 工厂方法创建代理
    public <T> T createProxy(T target, InvocationHandler handler) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            handler
        );
    }
}

结语

设计模式是软件开发中的重要工具,合理使用可以提高代码的可维护性、可扩展性和可读性。本文详细介绍了单例模式、工厂模式和代理模式的核心概念、实现方式和应用场景。通过大量的代码示例和详细解释,相信您已经对这三个模式有了深入的理解。在实际开发中,应该根据具体场景选择合适的模式,并注意模式组合使用时的权衡和取舍

相关推荐
闻哥2 小时前
Docker Swarm 负载均衡深度解析:VIP vs DNSRR 模式详解
java·运维·jvm·docker·容器·负载均衡
小林学编程2 小时前
模型上下文协议(MCP)的理解
java·后端·llm·prompt·resource·tool·mcp协议
软泡芙3 小时前
【Bug】ReactiveUI WPF绑定中依赖属性不更新的问题分析与解决方案
java·bug·wpf
小程故事多_803 小时前
Harness实战指南,在Java Spring Boot项目中规范落地OpenSpec+Claude Code
java·人工智能·spring boot·架构·aigc·ai编程
浪扼飞舟3 小时前
WPF输入验证(ValidationRule)
java·javascript·wpf
砍材农夫8 小时前
spring-ai 第四多模态API
java·人工智能·spring
她说..10 小时前
Java 对象相关高频面试题
java·开发语言·spring·java-ee
庞轩px11 小时前
深入理解 sleep() 与 wait():从基础到监视器队列
java·开发语言·线程··wait·sleep·监视器
皮皮林55111 小时前
面试官:ZSet 的底层实现是什么?
java