《常见的设计模式——单例、代理与适配器》
目录
引言
一、单例模式(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 {@Overridepublic void request() {System.out.println("真实请求");}
}class Proxy implements Subject {private RealSubject realSubject;@Overridepublic 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 {@Overridepublic void dynamicRequest() {System.out.println("真实动态请求");}
}class DynamicProxyHandler implements InvocationHandler {private Object target;public DynamicProxyHandler(Object target) {this.target = target;}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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;}@Overridepublic void request() {adaptee.specificRequest();}
}
(3) 接口适配器(缺省适配器)
interface ServiceInterface {void service1();void service2();void service3();
}abstract class AbstractAdapter implements ServiceInterface {@Overridepublic void service1() {}@Overridepublic void service2() {}@Overridepublic void service3() {}
}class ConcreteService extends AbstractAdapter {@Overridepublic void service1() {System.out.println("只实现需要的服务1");}
}
3. 应用场景
场景类型 | 示例 |
---|---|
旧系统整合 | 将遗留系统接口适配到新系统 |
第三方库适配 | 统一不同库的接口规范 |
接口版本兼容 | 新老接口版本兼容处理 |
设备驱动适配 | 不同厂商设备驱动适配统一接口 |
数据格式转换 | XML与JSON数据转换 |
4. 实际案例
JDBC驱动管理器:适配不同数据库的驱动
Spring MVC的HandlerAdapter:适配不同类型的控制器
Java I/O中的InputStreamReader:将字节流适配为字符流
四、三种模式对比
模式 | 核心目的 | 主要优点 | 典型应用场景 |
---|---|---|---|
单例模式 | 控制实例数量 | 全局访问、节省资源 | 配置管理、连接池 |
代理模式 | 控制对象访问 | 职责清晰、扩展性强 | 权限控制、AOP编程 |
适配器 | 接口转换 | 提高复用性、兼容性强 | 系统整合、第三方库适配 |
五、模式选择建议
-
单例模式选择:
- 需要严格控制实例数量时
- 考虑线程安全性和性能(推荐双重检查锁或静态内部类实现)
-
代理模式选择:
- 需要增强或控制对象功能时
- 静态代理用于简单场景,动态代理用于复杂场景
- Spring AOP优先于手动代理
-
适配器模式选择:
- 接口不兼容需要转换时
- 优先使用对象适配器(组合优于继承)
- 考虑适配器的透明性(是否对客户端暴露适配细节)
记住:应根据实际需求合理选择,保持代码的简洁性和可维护性。