【HeadFirst系列之HeadFirst设计模式】第13天之代理模式:控制对象访问的利器!

代理模式:控制对象访问的利器!

大家好!今天我们来聊聊设计模式中的代理模式 (Proxy Pattern)。如果你曾经需要控制对某个对象的访问,或者在访问对象时添加额外的逻辑(如权限检查、日志记录等),那么代理模式就是你的救星!本文基于《Head First 设计模式》的代理模式章节,通过生动的故事和 Java 代码示例 ,带你轻松掌握代理模式的精髓。我们还会探讨代理模式在 JDKSpring 等框架中的应用,让你真正理解它的实际价值。


1. 代理模式是什么?

代理模式是一种结构型设计模式,它允许通过一个代理对象来控制对另一个对象的访问。代理模式的核心思想是在不改变原始类的情况下,通过引入代理类来增强或控制对原始类的访问

适用场景

  • 需要控制对某个对象的访问(如权限检查、延迟加载等)。
  • 需要在访问对象时添加额外的逻辑(如日志记录、性能监控等)。
  • 需要为远程对象提供本地代理(如远程方法调用 RMI)。

2. 代理模式的实现

故事背景

小明开发了一个图片查看器系统,系统中有一个 Image 类用于加载和显示图片。由于图片加载是一个耗时的操作,小明希望在图片真正显示之前,先显示一个占位符(如加载中...),并在图片加载完成后再显示实际图片。

问题出现

如果直接在 Image 类中实现加载逻辑,会导致图片查看器的性能下降,因为每次加载图片时都需要等待图片加载完成。

解决方案:代理模式

小明决定使用代理模式,通过引入一个代理类 ImageProxy 来控制对 Image 对象的访问。代理类可以在图片加载完成前显示占位符,并在图片加载完成后显示实际图片。


3. 代码实现

1. 定义接口

首先,我们定义一个接口 Image,包含图片显示的方法。

java 复制代码
// 图片接口
interface Image {
    void display();
}

2. 实现真实对象

接下来,我们实现真实的图片类 RealImage,用于加载和显示图片。

java 复制代码
// 真实图片类
class RealImage implements Image {
    private String filename;

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

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

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

3. 实现代理对象

然后,我们实现代理类 ImageProxy,用于控制对 RealImage 对象的访问。

java 复制代码
// 图片代理类
class ImageProxy implements Image {
    private String filename;
    private RealImage realImage;

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

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename); // 延迟加载
        }
        realImage.display();
    }
}

4. 客户端代码

最后,我们编写客户端代码来测试代理模式。

java 复制代码
public class ImageViewer {
    public static void main(String[] args) {
        Image image = new ImageProxy("photo.jpg");

        // 图片未加载时,代理类可以显示占位符
        System.out.println("Image will be loaded on demand.");

        // 第一次显示图片,触发加载
        image.display(); // 输出: Loading image: photo.jpg, Displaying image: photo.jpg

        // 第二次显示图片,无需重新加载
        image.display(); // 输出: Displaying image: photo.jpg
    }
}

4. 代理模式的优缺点

优点

  • 职责清晰:代理类负责控制对真实对象的访问,真实对象只需关注核心逻辑。
  • 延迟加载:可以通过代理类实现延迟加载,提高系统性能。
  • 增强功能:可以在不修改真实对象的情况下,通过代理类添加额外的功能(如权限检查、日志记录等)。

缺点

  • 增加复杂度:引入代理类会增加系统的复杂度。
  • 性能开销:代理类可能会引入额外的性能开销(如方法调用的间接性)。

5. 代理模式在 JDK 和 Spring 中的应用

1. JDK 中的应用

  • 动态代理 :JDK 提供了 java.lang.reflect.Proxy 类,用于创建动态代理对象。动态代理可以在运行时生成代理类,常用于实现 AOP(面向切面编程)。
  • RMI(远程方法调用):JDK 的 RMI 框架使用代理模式来实现远程对象的本地代理。
动态代理示例
java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 接口
interface Greeting {
    void sayHello();
}

// 真实对象
class GreetingImpl implements Greeting {
    @Override
    public void sayHello() {
        System.out.println("Hello, World!");
    }
}

// 动态代理处理器
class LoggingHandler implements InvocationHandler {
    private Object target;

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

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

// 客户端代码
public class DynamicProxyDemo {
    public static void main(String[] args) {
        Greeting realObject = new GreetingImpl();
        Greeting proxy = (Greeting) Proxy.newProxyInstance(
            realObject.getClass().getClassLoader(),
            realObject.getClass().getInterfaces(),
            new LoggingHandler(realObject)
        );

        proxy.sayHello(); // 输出: Before method: sayHello, Hello, World!, After method: sayHello
    }
}

2. Spring 中的应用

  • AOP(面向切面编程):Spring 的 AOP 模块使用代理模式来实现切面功能(如日志记录、事务管理等)。Spring 支持 JDK 动态代理和 CGLIB 代理。
  • 事务管理:Spring 的事务管理器通过代理模式来控制事务的开启、提交和回滚。
Spring AOP 示例
java 复制代码
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.NameMatchMethodPointcut;

// 接口
interface Service {
    void perform();
}

// 真实对象
class ServiceImpl implements Service {
    @Override
    public void perform() {
        System.out.println("Performing service...");
    }
}

// 切面类
class LoggingAspect {
    public void before() {
        System.out.println("Before method execution.");
    }

    public void after() {
        System.out.println("After method execution.");
    }
}

// 客户端代码
public class SpringAOPDemo {
    public static void main(String[] args) {
        Service realObject = new ServiceImpl();
        ProxyFactory proxyFactory = new ProxyFactory(realObject);

        // 创建切点
        NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
        pointcut.addMethodName("perform");

        // 创建切面
        LoggingAspect aspect = new LoggingAspect();
        proxyFactory.addAdvisor(new DefaultPointcutAdvisor(pointcut, (method, args1, target) -> {
            aspect.before();
            Object result = method.invoke(target, args1);
            aspect.after();
            return result;
        }));

        // 获取代理对象
        Service proxy = (Service) proxyFactory.getProxy();
        proxy.perform(); // 输出: Before method execution., Performing service..., After method execution.
    }
}

6. 总结

代理模式通过引入代理类来控制对真实对象的访问,从而在不修改原始类的情况下增强或控制其行为。它的核心优势在于:

  • 职责分离:代理类负责控制访问,真实对象专注于核心逻辑。
  • 延迟加载:通过代理类实现延迟加载,提升系统性能。
  • 增强功能:通过代理类添加额外功能(如日志记录、权限检查等)。

希望本文能帮助你更好地理解代理模式,并在实际项目中灵活运用!


互动话题

你在项目中使用过代理模式吗?遇到过哪些有趣的问题?欢迎在评论区分享你的经验!

相关推荐
阿波茨的鹅2 小时前
C++ | 设计模式 | 代理模式
c++·设计模式·代理模式
_真相只有一个3 小时前
结构型模式 - 代理模式 (Proxy Pattern)
设计模式·代理模式
Normal Developer3 小时前
设计模式-结构性模式
设计模式
扣丁梦想家3 小时前
设计模式教程:模板方法模式(Template Method Pattern)
java·设计模式·模板方法模式
萧萧玉树4 小时前
设计模式的引入
设计模式
多多*8 小时前
设计模式 工厂模式 工厂方法模式 抽象工厂模式
设计模式·工厂方法模式·抽象工厂模式
啥都不懂的小小白9 小时前
Java常见设计模式(上):创建型模式
java·开发语言·设计模式
花王江不语10 小时前
**模式的好处 (设计模式)
开发语言·设计模式
galaa201110 小时前
TypeScript设计模式(4):装饰器模式
设计模式