《常见的设计模式——单例、代理与适配器》

发布于:2025-09-13 ⋅ 阅读:(13) ⋅ 点赞:(0)

目录

引言

一、单例模式(Singleton)

二、代理模式(Proxy)

三、适配器模式(Adapter)

四、三种模式对比

五、模式选择建议


引言

设计模式是解决特定问题的经典方案,本文将聚焦三种最常用的设计模式:单例模式、代理模式和适配器模式,通过实例代码和实际应用场景,帮助你深入理解它们的精髓。

一、单例模式(Singleton)

1. 模式定义

单例模式确保一个类只有一个实例,并提供一个全局访问点。这是所有设计模式中最简单但应用最广泛的一种。

2. 实现方式

(1) 饿汉式(线程安全)

public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();
    
    private EagerSingleton() {}
    
    public static EagerSingleton getInstance() {
        return instance;
    }
}

特点:类加载时就初始化,线程安全但可能造成资源浪费

(2) 懒汉式(双重检查锁)

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

特点:延迟加载,线程安全且高效(推荐)

(3) 静态内部类实现

public class InnerClassSingleton {
    private InnerClassSingleton() {}
    
    private static class SingletonHolder {
        private static final InnerClassSingleton instance = new InnerClassSingleton();
    }
    
    public static InnerClassSingleton getInstance() {
        return SingletonHolder.instance;
    }
}

特点:兼顾延迟加载和线程安全,无需同步锁

3. 应用场景

配置管理类(如Spring的ApplicationContext)

数据库连接池

日志记录器

线程池

缓存系统

4. 注意事项

多线程环境必须保证线程安全

考虑序列化和反序列化破坏单例的问题

反射攻击防护(可在构造器中检查实例是否存在)

二、代理模式(Proxy)

1. 模式定义

代理模式为其他对象提供一种代理控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用。

2. 实现方式

(1) 静态代理

interface Subject {
    void request();
}

class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("真实请求");
    }
}

class Proxy implements Subject {
    private RealSubject realSubject;
    
    @Override
    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        preRequest();
        realSubject.request();
        postRequest();
    }
    
    private void preRequest() {
        System.out.println("预处理");
    }
    
    private void postRequest() {
        System.out.println("后处理");
    }
}

(2) 动态代理(JDK实现)

interface DynamicSubject {
    void dynamicRequest();
}

class RealDynamicSubject implements DynamicSubject {
    @Override
    public void dynamicRequest() {
        System.out.println("真实动态请求");
    }
}

class DynamicProxyHandler implements InvocationHandler {
    private Object target;
    
    public DynamicProxyHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("动态代理预处理");
        Object result = method.invoke(target, args);
        System.out.println("动态代理后处理");
        return result;
    }
}

// 使用方式
DynamicSubject subject = (DynamicSubject) Proxy.newProxyInstance(
    ClassLoader.getSystemClassLoader(),
    new Class[]{DynamicSubject.class},
    new DynamicProxyHandler(new RealDynamicSubject())
);
subject.dynamicRequest();

(3) CGLIB动态代理

class RealService {
    public void execute() {
        System.out.println("真实服务执行");
    }
}

class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CGLIB预处理");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("CGLIB后处理");
        return result;
    }
}

// 使用方式
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealService.class);
enhancer.setCallback(new CglibProxy());
RealService proxy = (RealService) enhancer.create();
proxy.execute();

3. 代理类型

代理类型

特点

远程代理

为不同地址空间的对象提供本地代表(如RMI)

虚拟代理

根据需要创建开销大的对象(如图片懒加载)

保护代理

控制对原始对象的访问权限

智能引用

在访问对象时执行额外操作(如引用计数、懒加载)

Spring AOP

基于动态代理实现的面向切面编程

4. 应用场景

权限控制

延迟加载

日志记录

事务管理

性能监控

RPC调用

三、适配器模式(Adapter)

1. 模式定义

适配器模式将一个类的接口转换成客户期望的另一个接口,使原本接口不兼容的类可以一起工作。

2. 实现方式

(1) 类适配器(继承方式)

class Adaptee {
    public void specificRequest() {
        System.out.println("特殊请求");
    }
}

interface Target {
    void request();
}

class Adapter extends Adaptee implements Target {
    @Override
    public void request() {
        specificRequest();
    }
}

(2) 对象适配器(组合方式,推荐)

class Adaptee {
    public void specificRequest() {
        System.out.println("特殊请求");
    }
}

interface Target {
    void request();
}

class Adapter implements Target {
    private Adaptee adaptee;
    
    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    
    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

(3) 接口适配器(缺省适配器)

interface ServiceInterface {
    void service1();
    void service2();
    void service3();
}

abstract class AbstractAdapter implements ServiceInterface {
    @Override
    public void service1() {}
    
    @Override
    public void service2() {}
    
    @Override
    public void service3() {}
}

class ConcreteService extends AbstractAdapter {
    @Override
    public void service1() {
        System.out.println("只实现需要的服务1");
    }
}

3. 应用场景

场景类型

示例

旧系统整合

将遗留系统接口适配到新系统

第三方库适配

统一不同库的接口规范

接口版本兼容

新老接口版本兼容处理

设备驱动适配

不同厂商设备驱动适配统一接口

数据格式转换

XML与JSON数据转换

4. 实际案例

JDBC驱动管理器:适配不同数据库的驱动

Spring MVC的HandlerAdapter:适配不同类型的控制器

Java I/O中的InputStreamReader:将字节流适配为字符流

四、三种模式对比

模式 核心目的 主要优点 典型应用场景
单例模式 控制实例数量 全局访问、节省资源 配置管理、连接池
代理模式 控制对象访问 职责清晰、扩展性强 权限控制、AOP编程
适配器 接口转换 提高复用性、兼容性强 系统整合、第三方库适配

五、模式选择建议

  1. 单例模式选择:

    • 需要严格控制实例数量时
    • 考虑线程安全性和性能(推荐双重检查锁或静态内部类实现)
  2. 代理模式选择:

    • 需要增强或控制对象功能时
    • 静态代理用于简单场景,动态代理用于复杂场景
    • Spring AOP优先于手动代理
  3. 适配器模式选择:

    • 接口不兼容需要转换时
    • 优先使用对象适配器(组合优于继承)
    • 考虑适配器的透明性(是否对客户端暴露适配细节)

记住:应根据实际需求合理选择,保持代码的简洁性和可维护性。


网站公告

今日签到

点亮在社区的每一天
去签到