【代理模式】设计模式系列:实现与最佳实践(掌控访问的艺术)

Java中的代理模式

引言

1. 设计模式的重要性

设计模式是在软件工程领域内经过验证的解决方案,它们解决的是在特定情境下反复出现的设计问题。通过采用设计模式,开发者可以更好地组织代码,提高程序的可读性、可维护性和可扩展性。设计模式通常包含三个基本要素:模式名称、问题描述以及解决方案。它们为常见的软件设计问题提供了标准的命名、已知的系统配置以及如何解决这些问题的实例。

2. 为什么使用代理模式

代理模式是一种结构型设计模式,它提供了一个代理对象来控制对目标对象的访问。代理模式的主要目的是在访问目标对象之前或之后添加一些额外的操作,比如权限检查、日志记录、缓存数据等。使用代理模式可以帮助我们实现职责分离,提高系统的灵活性和可扩展性。

3. 应用场景概述

代理模式广泛应用于多种场合,例如:

  • 远程代理:代表一个远端的对象。
  • 虚拟代理:用来替代开销很大的对象。
  • 缓存代理:存储目标对象的副本以提高性能。
  • 保护代理:控制对目标对象的访问权限。
  • 智能引用代理:当对象被引用时做一些额外的工作,如跟踪引用次数。

1. 代理模式概念

1.1 代理模式定义

代理模式定义了一个代理类来代表真实的服务对象,这样可以通过代理类间接地访问服务对象,而不是直接访问。代理类可以用来封装对真实对象的访问,并且可以在访问前后执行一些额外的操作。

1.2 代理模式的参与者

  • 接口(Subject):定义了真实对象和代理对象共同遵循的接口,以便客户端能够透明地使用这些对象。
  • 实体(RealSubject):实现了Subject接口的真实对象,即客户端最终要访问的目标对象。
  • 代理(Proxy):同样实现了Subject接口的代理对象,它负责控制对真实对象的访问,并且可以在访问前后添加一些额外的操作。
    在这里插入图片描述
    在这里插入图片描述

1.3 代理模式的基本工作原理

  1. 定义接口:首先定义一个接口,这个接口定义了真实对象和代理对象的行为。
  2. 实现接口:真实对象实现这个接口,提供具体的功能实现。
  3. 创建代理类:代理类也实现了相同的接口,并持有真实对象的引用。
  4. 客户端调用:客户端通过接口来调用方法,无论是真实对象还是代理对象。
  5. 代理操作:代理类在调用真实对象的方法之前或之后可以执行一些额外的操作。

1.4 代理模式的优点与缺点

1. 优点:

  • 职责分离:代理对象承担了一些额外的责任,如缓存、日志记录等,使得真实对象更加简洁。
  • 扩展性:通过代理模式可以方便地扩展功能,而无需修改原始代码。
  • 安全性:可以通过代理对象实现访问控制,提高系统的安全性。
  • 性能提升:例如,缓存代理可以显著提高应用程序的性能。

2. 缺点:

  • 增加了复杂性:引入代理会增加系统的复杂度。
  • 潜在的性能影响:代理对象的额外操作可能会带来一定的性能开销。

2. Java代理模式实现方式

2.1 静态代理

1. 定义

静态代理是指在程序编译期就已经确定代理类的实现方式。这意味着代理类和真实对象类都是在编写代码时就已经定义好的,并且不会改变。这种类型的代理模式通常用于简单的场景,其中代理类和真实对象类具有固定的接口。

2. 示例代码

假设我们有一个Subject接口,该接口定义了所有代理对象和真实对象都应遵循的行为。

// Subject接口
public interface Subject {
    void request();
}

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

// Proxy实现Subject接口
public class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        // 执行前的操作
        System.out.println("Proxy: Preparing to handle request.");

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

        // 执行后的操作
        System.out.println("Proxy: Finishing handling request.");
    }
}

3. 客户端代码如下:

public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Proxy proxy = new Proxy(realSubject);
        proxy.request();
    }
}

4. 优缺点

优点:

  • 简单易懂:代理类和真实对象类都是在编译时定义的,因此易于理解和实现。
  • 类型安全:由于代理类和真实对象类遵循相同的接口,因此可以确保类型安全。

缺点:

  • 灵活性较低:如果需要修改代理行为,则需要修改并重新编译代理类。
  • 代码耦合:如果接口发生变化,代理类和真实对象类都需要进行相应的更改。

2.2 动态代理

2.2.1 JDK动态代理

1. 原理

JDK动态代理允许我们在运行时创建一个实现给定接口的代理对象。这个代理对象可以动态地拦截方法调用,并在调用前后执行额外的操作。JDK动态代理是基于Java反射API实现的。

2. 实现步骤

  1. 定义接口:定义一个接口,该接口将被代理对象和真实对象共同实现。
  2. 创建InvocationHandler:创建一个实现了InvocationHandler接口的类,该类包含对真实对象的引用,并定义了方法拦截逻辑。
  3. 获取代理对象:使用Proxy.newProxyInstance()方法创建代理对象。

3. 示例代码

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// Subject接口
public interface Subject {
    void request();
}

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

// 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("MyInvocationHandler: Preparing to handle request.");

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

        // 执行后的操作
        System.out.println("MyInvocationHandler: Finishing handling request.");

        return result;
    }
}

public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        InvocationHandler handler = new MyInvocationHandler(realSubject);
        Subject proxy = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                handler
        );
        proxy.request();
    }
}
2.2.2 CGLIB动态代理

1. 原理

CGLIB(Code Generation Library)是一个强大的高性能的代码生成库。它可以在运行时动态生成代理类。与JDK动态代理不同的是,CGLIB并不依赖于接口,而是通过继承的方式实现代理。

2. 实现步骤

  1. 导入CGLIB库:确保项目中包含了CGLIB库。
  2. 创建Enhancer:使用CGLIB的Enhancer类来创建代理对象。
  3. 设置Callback:定义一个实现了MethodInterceptor接口的类,该类将用于拦截方法调用。

3. 示例代码

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// RealSubject
public class RealSubject {
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// MethodInterceptor实现
public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 执行前的操作
        System.out.println("MyMethodInterceptor: Preparing to handle request.");

        // 调用真实对象的方法
        Object result = proxy.invokeSuper(obj, args);

        // 执行后的操作
        System.out.println("MyMethodInterceptor: Finishing handling request.");

        return result;
    }
}

public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(realSubject.getClass());
        enhancer.setCallback(new MyMethodInterceptor());
        RealSubject proxy = (RealSubject) enhancer.create();
        proxy.request();
    }
}

2.3 两者之间的区别与选择

1. JDK动态代理与CGLIB动态代理的区别:

  • 接口依赖:JDK动态代理依赖于接口,而CGLIB动态代理不需要接口,它通过继承实现。
  • 性能:CGLIB在某些情况下可能会比JDK动态代理稍微慢一点,因为它涉及到类的继承。
  • 适用场景:如果你的应用程序使用了大量的接口,那么JDK动态代理可能是更好的选择。如果你的应用程序中类没有实现接口,或者需要更深层次的代理,那么CGLIB可能是更好的选择。

2. 选择指南:

  • 如果你的类已经实现了接口,而且你不想改变现有的类结构,那么使用JDK动态代理。
  • 如果你的类没有实现接口,或者你需要在不修改现有类的情况下添加代理行为,那么使用CGLIB动态代理。

3. 使用案例分析

3.1 缓存代理

1. 场景描述

缓存代理是一种常用的优化技术,用于减少访问昂贵资源的成本。例如,在Web应用程序中,从数据库或远程服务器获取数据可能非常耗时。为了提高性能,可以使用缓存代理来存储最近或经常访问的数据副本,从而避免每次都从原始数据源获取数据。

2. 代码实现

假设我们有一个DataFetcher接口,代表了一个可以从远程服务器获取数据的服务。

public interface DataFetcher {
    String fetchData(int id);
}

// RealDataFetcher 实现 DataFetcher 接口
public class RealDataFetcher implements DataFetcher {
    @Override
    public String fetchData(int id) {
        try {
            Thread.sleep(2000); // 模拟延迟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Data for ID " + id;
    }
}

// CacheProxy 实现 DataFetcher 接口
public class CacheProxy implements DataFetcher {
    private Map<Integer, String> cache = new HashMap<>();
    private DataFetcher realFetcher;

    public CacheProxy(DataFetcher realFetcher) {
        this.realFetcher = realFetcher;
    }

    @Override
    public String fetchData(int id) {
        if (!cache.containsKey(id)) {
            String data = realFetcher.fetchData(id);
            cache.put(id, data);
            return data;
        }
        return cache.get(id);
    }
}

3. 客户端代码如下:

public class Client {
    public static void main(String[] args) {
        DataFetcher realFetcher = new RealDataFetcher();
        DataFetcher cacheProxy = new CacheProxy(realFetcher);

        long startTime = System.currentTimeMillis();
        System.out.println(cacheProxy.fetchData(1));
        System.out.println(cacheProxy.fetchData(2));
        System.out.println(cacheProxy.fetchData(1)); // 第二次请求ID 1,应该从缓存中读取
        long endTime = System.currentTimeMillis();

        System.out.println("Total time taken: " + (endTime - startTime) + "ms");
    }
}

3.2 远程代理

1. 场景描述

远程代理用于隐藏一个对象位于不同的地址空间的事实。当一个对象位于远程服务器上时,远程代理可以在本地客户端充当该对象的替身,处理所有远程通信细节。

2. 代码实现

这里我们使用RMI(Remote Method Invocation)作为远程代理的一个例子。为了简化,我们将展示一个基本的客户端-服务器模型,其中服务器提供了一个接口,客户端通过远程代理访问它。

3. 服务器端代码

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public interface RemoteService extends java.rmi.Remote {
    String getData() throws RemoteException;
}

public class RemoteServiceImpl extends UnicastRemoteObject implements RemoteService {
    protected RemoteServiceImpl() throws RemoteException {
        super();
    }

    @Override
    public String getData() throws RemoteException {
        return "Data from remote server";
    }
}

public class Server {
    public static void main(String[] args) {
        try {
            RemoteService service = new RemoteServiceImpl();
            Naming.rebind("//localhost/RemoteService", service);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4. 客户端代码

import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class Client {
    public static void main(String[] args) {
        try {
            RemoteService service = (RemoteService) Naming.lookup("//localhost/RemoteService");
            System.out.println(service.getData());
        } catch (NotBoundException | RemoteException e) {
            e.printStackTrace();
        }
    }
}

3.3 虚拟代理

1. 场景描述

虚拟代理用于代替一个大对象或开销大的对象。它只在真正需要的时候才去创建或加载那个对象。这样可以节省内存和其他资源。

2. 代码实现

假设我们有一个大型图片对象,我们想要延迟加载这个对象直到它真正被需要。

public interface Image {
    void display();
}

// RealImage 实现 Image 接口
public class RealImage implements Image {
    private final String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk(filename);
    }

    private void loadFromDisk(String filename) {
        System.out.println("Loading image from disk: " + filename);
    }

    @Override
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

// VirtualProxy 实现 Image 接口
public class VirtualProxy implements Image {
    private RealImage realImage;
    private String filename;

    public VirtualProxy(String filename) {
        this.filename = filename;
    }

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

3. 客户端代码如下:

public class Client {
    public static void main(String[] args) {
        Image virtualProxy = new VirtualProxy("big_image.jpg");
        virtualProxy.display(); // 第一次调用时会加载图片
        virtualProxy.display(); // 第二次调用时不会重复加载
    }
}

3.4 智能引用代理

1. 场景描述

智能引用代理不仅可以控制对象的创建,还可以在代理对象中添加一些额外的功能,比如计数引用次数、追踪对象的生命周期等。

2. 代码实现

我们可以通过一个简单的计数器来实现一个智能引用代理。

public interface Countable {
    void incrementCount();
    int getCount();
    void print();
}

// RealCountable 实现 Countable 接口
public class RealCountable implements Countable {
    private int count = 0;

    @Override
    public void incrementCount() {
        count++;
    }

    @Override
    public int getCount() {
        return count;
    }

    @Override
    public void print() {
        System.out.println("Printed " + count + " times.");
    }
}

// SmartReferenceProxy 实现 Countable 接口
public class SmartReferenceProxy implements Countable {
    private Countable realCountable;
    private int referenceCount = 0;

    public SmartReferenceProxy(Countable realCountable) {
        this.realCountable = realCountable;
    }

    @Override
    public void incrementCount() {
        referenceCount++;
        realCountable.incrementCount();
    }

    @Override
    public int getCount() {
        return realCountable.getCount();
    }

    @Override
    public void print() {
        System.out.println("Reference count: " + referenceCount);
        realCountable.print();
    }
}

3. 客户端代码如下:

public class Client {
    public static void main(String[] args) {
        Countable realCountable = new RealCountable();
        Countable smartReferenceProxy = new SmartReferenceProxy(realCountable);

        smartReferenceProxy.incrementCount();
        smartReferenceProxy.incrementCount();
        smartReferenceProxy.print();

        smartReferenceProxy.incrementCount();
        smartReferenceProxy.print();
    }
}

4. 代理模式在实际开发中的应用

4.1 AOP(面向切面编程)与代理模式

1. AOP简介

面向切面编程(Aspect-Oriented Programming,AOP)是一种编程范式,它旨在将横切关注点(cross-cutting concerns)从业务逻辑中分离出来。在Java中,AOP通常通过代理模式来实现,其中代理对象负责处理横切关注点,比如日志记录、事务管理等。

2. Spring AOP与代理模式

Spring框架提供了强大的AOP支持,它使用代理模式来实现AOP的功能。Spring AOP支持两种代理模式:基于JDK的动态代理和基于CGLIB的动态代理。

3. 基于JDK的动态代理

Spring AOP默认使用JDK动态代理,适用于那些实现了接口的类。Spring通过ProxyFactoryBean@AspectJ注解来创建代理对象。

4. 基于CGLIB的动态代理

对于没有实现接口的类,Spring可以使用CGLIB来创建代理。在这种情况下,代理对象将继承原始类,并通过方法拦截器来处理横切关注点。

5. 示例代码

假设我们有一个UserService接口和其实现类UserServiceImpl

public interface UserService {
    User getUserById(int id);
}

public class UserServiceImpl implements UserService {
    @Override
    public User getUserById(int id) {
        System.out.println("Fetching user with ID: " + id);
        return new User(id, "John Doe");
    }
}

使用Spring AOP来为UserServiceImpl创建一个代理,添加日志记录切面。

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LoggingAspect {

    @Around("execution(* com.example.service.UserServiceImpl.getUserById(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
        Object result = joinPoint.proceed();
        System.out.println("After method: " + joinPoint.getSignature().getName());
        return result;
    }
}

配置Spring AOP,使它能够识别LoggingAspect

<!-- Spring configuration file -->
<bean id="loggingAspect" class="com.example.aspect.LoggingAspect" />

<aop:config>
    <aop:aspect ref="loggingAspect">
        <aop:pointcut id="userServiceMethods"
                      expression="execution(* com.example.service.UserServiceImpl.*(..))"/>
        <aop:around pointcut-ref="userServiceMethods" method="logAround"/>
    </aop:aspect>
</aop:config>

4.2 Spring框架中的代理模式应用

除了AOP之外,Spring框架还广泛使用代理模式来实现诸如依赖注入(DI)、事务管理等功能。

1. 依赖注入

Spring使用代理模式来实现依赖注入,特别是在使用基于接口的依赖注入时。Spring容器可以通过创建代理对象来控制对bean的访问,并在注入时添加额外的行为。

2. 事务管理

Spring的事务管理也利用了代理模式。当使用声明式事务管理时,Spring会为需要事务管理的方法创建一个代理,这个代理会在方法调用前后自动管理事务。

3. 示例代码

使用Spring的@Transactional注解来自动管理事务。

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderService {
    @Transactional
    public void placeOrder(Order order) {
        // Place the order logic
        System.out.println("Placing order: " + order);
    }
}

4.3 Hibernate ORM中的代理模式应用

Hibernate是一个流行的Java ORM框架,它使用代理模式来实现延迟加载(lazy loading)。

1. 延迟加载

在Hibernate中,当我们使用lazy="true"属性来配置一个关联关系时,Hibernate会为这个关联创建一个代理对象。只有当真正需要关联对象的数据时,Hibernate才会发起数据库查询来加载这些数据。

2. 示例代码

假设我们有两个实体DepartmentEmployee,并且Department包含了一个懒加载的Set<Employee>集合。

@Entity
public class Department {
    @Id
    private int id;
    private String name;

    @OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
    private Set<Employee> employees = new HashSet<>();

    // Getters and setters
}

@Entity
public class Employee {
    @Id
    private int id;
    private String name;

    @ManyToOne
    private Department department;

    // Getters and setters
}

3. 客户端代码如下:

Session session = sessionFactory.openSession();
Department department = session.get(Department.class, 1);
System.out.println(department.getName()); // 不会触发数据库查询
System.out.println(department.getEmployees()); // 触发数据库查询

4.4 其他Java框架中的代理模式应用

1. Struts2

Struts2框架使用代理模式来处理Action的调用。在Struts2中,Action类通常实现Action接口,而框架则使用动态代理来处理Action的调用过程。

2. EclipseLink

EclipseLink ORM框架也使用代理模式来实现延迟加载和一级缓存。

3. JPA

Java Persistence API(JPA)规范要求持久化提供者(如Hibernate)在使用延迟加载时必须使用代理模式。

4. 总结

代理模式是Java开发中非常重要的一个概念,它广泛应用于各种框架和工具中,以实现横切关注点的解耦、性能优化以及其他高级特性。掌握代理模式不仅有助于理解这些框架的工作原理,还能帮助开发者更好地设计和实现自己的应用程序。


5. 性能考虑

5.1 代理模式对性能的影响

1. 正向影响

  • 缓存机制:通过缓存代理可以避免不必要的计算或数据库访问,从而提高性能。
  • 负载均衡:在分布式系统中,代理可以作为负载均衡器,合理分配请求到不同的服务器,从而提高整体系统的响应速度。
  • 延迟加载:虚拟代理可以延迟加载大型对象,减少启动时的内存占用。

2. 负面影响

  • 额外的调用开销:每次通过代理访问真实对象时都会增加额外的调用开销,尤其是在代理中执行大量逻辑时。
  • 动态代理的性能损失:与静态代理相比,动态代理需要在运行时生成代理类,这可能会导致性能上的轻微下降。

5.2 如何优化代理模式

1. 减少代理中的额外操作

  • 最小化代理中的操作:尽量减少代理中执行的操作数量,仅在必要时执行。
  • 缓存结果:对于计算密集型的任务,可以考虑在代理中缓存结果,避免重复计算。

2. 使用高效的代理实现

  • 选择适当的代理类型:根据实际情况选择静态代理或动态代理,考虑使用JDK动态代理或CGLIB动态代理。
  • 性能监控与优化:定期进行性能监控,针对瓶颈进行优化。

3. 利用多线程

  • 异步处理:在代理中实现异步处理机制,如使用CompletableFuture或FutureTask,以减少主线程的等待时间。

5.3 性能测试与评估

1. 性能测试工具

  • JMeter:用于负载测试和性能测试,可以模拟大量用户并发访问。
  • Apache Bench:简单的命令行工具,用于测试Web服务器的性能。
  • Gatling:高性能的压力测试工具,支持多种协议。

2. 评估指标

  • 响应时间:测量从发送请求到收到响应的时间。
  • 吞吐量:单位时间内处理的请求数量。
  • 资源利用率:CPU、内存等资源的使用情况。

3. 实施步骤

  1. 基准测试:在没有代理的情况下进行基准测试,记录性能数据。
  2. 添加代理:逐步添加代理,并记录新的性能数据。
  3. 对比分析:比较有无代理的情况下的性能差异。
  4. 优化代理:根据测试结果优化代理的实现。
  5. 持续监控:部署后持续监控性能,确保代理没有引入新的性能瓶颈。

6. 代理模式的最佳实践

6.1 何时使用代理模式

  • 需要控制对对象的访问:当需要在访问对象之前或之后执行一些额外操作时。
  • 性能优化:例如,缓存代理可以减少不必要的计算或数据库访问。
  • 资源管理:例如,虚拟代理可以延迟加载大型对象。
  • 安全性:保护代理可以控制对敏感对象的访问权限。

6.2 避免滥用代理模式

  • 过度设计:不要为了使用设计模式而使用设计模式,除非确实有必要。
  • 过度代理:避免对每一个类都使用代理,仅在需要的地方使用。
  • 复杂度:过度使用代理模式可能会增加系统的复杂度,影响可维护性。

6.3 代理模式与其他设计模式的结合使用

  • 装饰者模式:装饰者模式和代理模式都可以用来包装对象,但装饰者模式更侧重于动态地添加责任。
  • 适配器模式:当需要将一个类转换成客户端期望的接口时,可以结合使用适配器模式和代理模式。
  • 工厂模式:可以使用工厂模式来创建代理对象,以确保代理对象的正确初始化和配置。

结合示例

假设我们需要创建一个缓存代理,同时还需要添加日志记录的功能。我们可以使用工厂模式来创建代理对象,并结合装饰者模式来添加日志记录的能力。

// Subject接口
public interface Subject {
    String fetchData(int id);
}

// RealSubject实现Subject接口
public class RealSubject implements Subject {
    @Override
    public String fetchData(int id) {
        try {
            Thread.sleep(2000); // 模拟延迟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Data for ID " + id;
    }
}

// CacheProxy 实现 Subject 接口
public class CacheProxy implements Subject {
    private Map<Integer, String> cache = new HashMap<>();
    private Subject realSubject;

    public CacheProxy(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public String fetchData(int id) {
        if (!cache.containsKey(id)) {
            String data = realSubject.fetchData(id);
            cache.put(id, data);
            return data;
        }
        return cache.get(id);
    }
}

// LoggingDecorator 实现 Subject 接口
public class LoggingDecorator implements Subject {
    private Subject subject;

    public LoggingDecorator(Subject subject) {
        this.subject = subject;
    }

    @Override
    public String fetchData(int id) {
        System.out.println("LoggingDecorator: Before fetching data");
        String data = subject.fetchData(id);
        System.out.println("LoggingDecorator: After fetching data");
        return data;
    }
}

// Factory for creating decorated proxies
public class ProxyFactory {
    public static Subject createDecoratedProxy(Subject realSubject) {
        Subject cacheProxy = new CacheProxy(realSubject);
        return new LoggingDecorator(cacheProxy);
    }
}

// Client code
public class Client {
    public static void main(String[] args) {
        Subject realFetcher = new RealSubject();
        Subject decoratedProxy = ProxyFactory.createDecoratedProxy(realFetcher);

        long startTime = System.currentTimeMillis();
        System.out.println(decoratedProxy.fetchData(1));
        System.out.println(decoratedProxy.fetchData(2));
        System.out.println(decoratedProxy.fetchData(1)); // 第二次请求ID 1,应该从缓存中读取
        long endTime = System.currentTimeMillis();

        System.out.println("Total time taken: " + (endTime - startTime) + "ms");
    }
}

在这个示例中,我们首先创建了一个缓存代理CacheProxy,然后使用装饰者模式通过LoggingDecorator来添加日志记录的功能。最后,我们通过工厂模式ProxyFactory来创建一个组合了缓存和日志记录功能的代理对象。

通过这种方式,我们可以灵活地组合不同的设计模式,以满足应用程序的需求。


7. 结论

1. 对代理模式的总结

代理模式是一种结构型设计模式,它允许开发者通过一个代理对象来间接访问另一个对象。这种模式的主要优点包括:

  • 控制对真实对象的访问;
  • 减少对象创建的数量;
  • 增加额外的行为(如日志记录、性能监控等);
  • 提供远程访问的能力;
  • 实现延迟加载和缓存机制。

然而,不当的使用也会带来一些负面影响,比如增加了系统的复杂度,以及可能的性能开销。

2. 未来发展趋势展望

  • 微服务架构:随着微服务架构的流行,代理模式在服务网关和服务发现等方面的应用将会越来越广泛。
  • 容器化与Kubernetes:在容器化环境下,代理模式可以用于网络代理和负载均衡。
  • 边缘计算:在边缘计算场景下,代理模式可以帮助处理设备间的通信,优化数据流。
  • 人工智能与机器学习:在AI领域,代理模式可以被用来控制对模型的访问,提供安全和隐私保护。

总体来说,代理模式作为一种经典的设计模式,将继续在软件开发中发挥重要作用,并且随着新技术的发展,它的应用场景将会不断扩展。


本文详细介绍了23种设计模式的基础知识,帮助读者快速掌握设计模式的核心概念,并找到适合实际应用的具体模式:
【设计模式入门】设计模式全解析:23种经典模式介绍与评级指南(设计师必备)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值