首页 新能源汽车

架构师解密:单例、多例、枚举、工厂与动态代理设计模式实战

字数: (6821)
阅读: (7320)
内容摘要:架构师解密:单例、多例、枚举、工厂与动态代理设计模式实战,

在软件开发中,设计模式是解决特定问题的经验总结。今天,我们将深入探讨几个常用的设计模式:单例设计模式多例设计模式、枚举的应用、工厂设计模式以及动态代理,并结合实际案例,分析其在后端架构中的应用和优缺点。希望通过本文,你能对这些设计模式有更深刻的理解,并在实际项目中灵活运用。

单例设计模式:保证唯一实例的艺术

问题场景:配置管理与连接池

想象一下,你的应用需要一个全局唯一的配置管理器,或者一个数据库连接池。如果每次都创建新的实例,会浪费资源,并可能导致数据不一致。这时,单例设计模式就派上了用场。

底层原理:限制实例化

单例设计模式的核心在于限制类的实例化,确保只有一个实例存在。通常的做法是将构造函数私有化,并提供一个静态方法来获取唯一的实例。

架构师解密:单例、多例、枚举、工厂与动态代理设计模式实战

代码实现:懒汉式与饿汉式

// 懒汉式单例
public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

// 饿汉式单例
public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

//双重校验锁(Double-Checked Locking)
public class Singleton {
    private volatile static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

懒汉式在第一次调用 getInstance() 方法时才创建实例,实现了延迟加载,但线程不安全。饿汉式在类加载时就创建实例,线程安全,但可能造成资源浪费。双重校验锁可以解决懒汉式的线程安全问题,同时保持延迟加载的优点。volatile 关键字保证可见性。

实战避坑:线程安全与序列化

  • 线程安全:在多线程环境下,要确保单例的线程安全性,可以使用 synchronized 关键字或双重校验锁。
  • 序列化:如果单例类需要序列化,要重写 readResolve() 方法,防止反序列化时创建新的实例。

多例设计模式:控制实例数量

问题场景:有限资源管理

有些场景下,我们需要限制类的实例数量,例如,连接池的大小是有限的。这时,多例设计模式就很有用。

架构师解密:单例、多例、枚举、工厂与动态代理设计模式实战

底层原理:维护实例集合

多例设计模式维护一个实例集合,并提供一个方法来获取实例。每次获取实例时,从集合中选择一个,或者创建新的实例,但总数不超过限制。

代码实现

public class MultiInstance {
    private static final int MAX_INSTANCES = 3; // 最大实例数
    private static final List<MultiInstance> instances = new ArrayList<>();
    private static int current = 0;

    private MultiInstance() {
        // 私有构造函数
    }

    static {
        for (int i = 0; i < MAX_INSTANCES; i++) {
            instances.add(new MultiInstance());
        }
    }

    public static MultiInstance getInstance() {
        current = (current + 1) % MAX_INSTANCES;
        return instances.get(current);
    }
}

实战避坑:线程安全与资源释放

  • 线程安全:在多线程环境下,要确保实例集合的线程安全性,可以使用 synchronized 关键字或 ConcurrentHashMap
  • 资源释放:在使用完实例后,要及时释放资源,避免资源泄漏。尤其当多例对象持有外部资源如数据库连接时,更需要注意。

枚举:类型安全的常量

问题场景:有限状态表示

在定义一组相关的常量时,使用枚举可以提高代码的可读性和类型安全性。例如,表示订单状态(待支付、已支付、已发货、已完成)。

架构师解密:单例、多例、枚举、工厂与动态代理设计模式实战

代码实现

public enum OrderStatus {
    PENDING_PAYMENT, // 待支付
    PAID,             // 已支付
    SHIPPED,          // 已发货
    COMPLETED         // 已完成
}

实战避坑:避免过度设计

  • 避免过度设计:不要为了使用枚举而使用枚举。只有在需要表示一组相关的常量时,才考虑使用枚举。
  • 灵活运用:Java 的枚举类可以拥有字段和方法,可以实现更复杂的功能,例如,根据状态执行不同的操作。

工厂设计模式:解耦对象创建

问题场景:复杂对象创建

当对象的创建过程复杂,或者需要根据不同的条件创建不同的对象时,可以使用工厂设计模式。这有助于解耦对象创建和使用。

底层原理:封装对象创建逻辑

工厂设计模式定义一个工厂类,负责创建对象。客户端只需要告诉工厂需要什么对象,而不需要关心对象的创建细节。

架构师解密:单例、多例、枚举、工厂与动态代理设计模式实战

代码实现:简单工厂、工厂方法、抽象工厂

  • 简单工厂:一个工厂类创建所有类型的对象。
  • 工厂方法:每个对象类型对应一个工厂类。
  • 抽象工厂:创建一系列相关对象。
// 简单工厂
public class SimpleFactory {
    public static Product createProduct(String type) {
        if (type.equals("A")) {
            return new ConcreteProductA();
        } else if (type.equals("B")) {
            return new ConcreteProductB();
        } else {
            return null;
        }
    }
}

// 工厂方法
interface Factory {
    Product create();
}

class ConcreteFactoryA implements Factory {
    @Override
    public Product create() {
        return new ConcreteProductA();
    }
}

实战避坑:选择合适的工厂模式

  • 选择合适的工厂模式:根据实际情况选择合适的工厂模式。如果对象类型较少且创建逻辑简单,可以使用简单工厂。如果对象类型较多且创建逻辑复杂,可以使用工厂方法或抽象工厂。
  • 避免过度抽象:不要为了使用工厂模式而过度抽象,增加代码的复杂度。

动态代理:增强对象行为

问题场景:AOP 与权限控制

在不修改原有代码的情况下,为对象添加额外的功能,例如,日志记录、性能监控、权限控制,可以使用动态代理

底层原理:运行时生成代理类

动态代理在运行时生成代理类,代理类实现了与目标类相同的接口,并在方法调用前后执行额外的逻辑。常见的就是JDK代理和CGLIB代理。JDK代理要求目标类必须实现接口,而CGLIB代理则没有这个限制。

代码实现

// JDK 动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Subject {
    void request();
}

class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

class DynamicProxy implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Proxy: Before invoking method.");
        Object result = method.invoke(target, args);
        System.out.println("Proxy: After invoking method.");
        return result;
    }
}

public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        DynamicProxy handler = new DynamicProxy(realSubject);
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                handler);
        proxySubject.request();
    }
}

//CGLIB 动态代理
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

class TargetObject {
    public String method1(String param) {
        System.out.println("method1 param:" + param); 
        return param;
    }

    public int method2(int num) {
        System.out.println("method2 num:" + num); 
        return num;
    }

    public void method3() {
        System.out.println("method3 execute");
    }

    @Override
    public String toString() {
        return "TargetObject []";
    }
}

class CglibProxy implements MethodInterceptor {

    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开始事务...");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("提交事务...");
        return result;
    }
}

实战避坑:选择合适的代理方式

  • 选择合适的代理方式:如果目标类实现了接口,可以使用 JDK 动态代理。否则,可以使用 CGLIB 代理。
  • 性能影响:动态代理会带来一定的性能损耗,需要在性能和灵活性之间进行权衡。

总结:

本文深入探讨了单例设计模式多例设计模式、枚举的应用、工厂设计模式以及动态代理这五种常用的设计模式,并结合实际案例分析了它们的应用和优缺点。合理运用这些设计模式,可以提高代码的可读性、可维护性和可扩展性,从而构建出更加健壮的后端架构。在实际开发中,需要根据具体场景选择合适的设计模式,并注意线程安全、资源释放等问题,才能真正发挥设计模式的优势。 记住,设计模式不是银弹,过度使用反而会增加代码的复杂性,所以要结合实际情况灵活运用。

架构师解密:单例、多例、枚举、工厂与动态代理设计模式实战

转载请注明出处: 脱发程序员

本文的链接地址: http://m.acea2.store/article/50152.html

本文最后 发布于2026-04-27 00:45:56,已经过了0天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 肝帝 10 小时前
    这篇文章讲得很透彻,从原理到实践都有,点赞!
  • 接盘侠 4 天前
    工厂模式那块,简单工厂、工厂方法、抽象工厂的区别也讲清楚了,收藏了。