目录
引言
设计模式是解决特定问题的经典方案,本文将聚焦三种最常用的设计模式:单例模式、代理模式和适配器模式,通过实例代码和实际应用场景,帮助你深入理解它们的精髓。
一、单例模式(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编程 |
适配器 | 接口转换 | 提高复用性、兼容性强 | 系统整合、第三方库适配 |
五、模式选择建议
单例模式选择:
- 需要严格控制实例数量时
- 考虑线程安全性和性能(推荐双重检查锁或静态内部类实现)
代理模式选择:
- 需要增强或控制对象功能时
- 静态代理用于简单场景,动态代理用于复杂场景
- Spring AOP优先于手动代理
适配器模式选择:
- 接口不兼容需要转换时
- 优先使用对象适配器(组合优于继承)
- 考虑适配器的透明性(是否对客户端暴露适配细节)
记住:应根据实际需求合理选择,保持代码的简洁性和可维护性。