代理模式(长文解析)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

前言

在软件开发中,我们经常需要通过某种机制间接访问对象,以实现权限控制、延迟加载、网络通信等目标。代理模式(Proxy Pattern)正是为这类需求设计的结构型设计模式。它像一位“中间人”,在客户端与目标对象之间建立桥梁,既能保护核心对象的安全,又能提升系统的灵活性。本文将通过通俗易懂的比喻、分步讲解和代码示例,帮助读者掌握代理模式的核心思想和应用场景。


核心概念与定义

代理模式的核心思想是通过代理对象间接访问目标对象,其本质是“控制访问权限”和“增强功能”。可以将其想象为一个“门卫系统”:

  • 门卫(代理对象):负责验证访客身份、记录访问日志、分发权限等。
  • 房间(目标对象):存放着需要被访问的核心资源。

代理模式的三个关键角色如下:

  1. 目标接口(Target):定义客户端与真实对象共同遵守的接口。
  2. 真实主体(Real Subject):实际执行核心功能的对象。
  3. 代理(Proxy):实现目标接口,并持有对真实主体的引用,负责拦截和转发请求。

代码结构示例

from abc import ABC, abstractmethod

class Image(ABC):
    @abstractmethod
    def display(self):
        pass

class RealImage(Image):
    def __init__(self, filename):
        self.filename = filename
        self.load_from_disk()  # 模拟加载大图
    
    def display(self):
        print(f"Displaying {self.filename}")

class ProxyImage(Image):
    def __init__(self, filename):
        self.filename = filename
        self.real_image = None  # 延迟加载
    
    def display(self):
        if self.real_image is None:
            self.real_image = RealImage(self.filename)
        self.real_image.display()

代理模式的四大常见类型

代理模式根据业务场景可分为四种典型子类型,每种类型都像一把钥匙,对应不同的“控制访问”需求。

1. 远程代理(Remote Proxy)

作用:隐藏对象位于远程服务器的事实,使客户端像调用本地对象一样访问网络资源。
比喻:就像通过快递员(代理)从外地仓库(真实对象)取货,用户无需知道快递的具体流程。

案例场景

class RemoteServiceProxy:
    def __init__(self, host):
        self.host = host
    
    def call_api(self, endpoint):
        # 模拟建立网络连接
        print(f"Connecting to {self.host}...")
        # 转发请求到真实服务
        return RealRemoteService(self.host).execute(endpoint)

2. 虚拟代理(Virtual Proxy)

作用:延迟加载(Lazy Loading)大资源,提升系统性能。
比喻:如同“占位符”——先显示低分辨率缩略图,再加载高清大图。

代码实现

class VirtualImageProxy(Image):
    def __init__(self, filename):
        self.filename = filename
        self.thumbnail = "placeholder.jpg"  # 占位图
    
    def display(self):
        print(f"Showing thumbnail: {self.thumbnail}")
        # 仅在用户明确操作时加载真实图片
        if user_clicks_view_full():
            super().display()

3. 保护代理(Protection Proxy)

作用:控制对敏感资源的访问权限,实现权限验证。
比喻:像“门卫”——只有持有有效证件的人才能进入会议室。

权限验证示例

class SecureFileProxy:
    def __init__(self, file_path, user):
        self.file_path = file_path
        self.user = user
    
    def read(self):
        if self.user.has_permission("read"):
            return RealFile(self.file_path).read()
        else:
            raise PermissionError("Access Denied")

4. 智能代理(Smart Proxy)

作用:在方法调用前后自动执行附加逻辑,如缓存、日志记录或性能监控。
比喻:像“智能助手”——在你拨打电话前自动记录通话时间,在通话后生成账单。

缓存优化案例

class CachedServiceProxy:
    def __init__(self, service):
        self.service = service
        self.cache = {}
    
    def get_data(self, key):
        if key in self.cache:
            print("Returning cached data")
            return self.cache[key]
        else:
            result = self.service.get_data(key)
            self.cache[key] = result
            return result

代理模式 vs 其他设计模式对比

与装饰器模式(Decorator Pattern)的异同

  • 相同点:两者都使用组合(Composition)持有目标对象,并实现相同接口。
  • 关键区别
    | 特征 | 代理模式 | 装饰器模式 |
    |---------------|------------------------|--------------------------|
    | 核心目标 | 控制访问权限 | 动态扩展功能 |
    | 关注点 | 中介控制 | 功能增强 |
    | 典型场景 | 权限验证、延迟加载 | 日志记录、性能监控 |

与适配器模式(Adapter Pattern)的差异

适配器模式解决“接口不兼容”问题,而代理模式解决“间接访问”需求。例如:

  • 适配器:将圆形插头(旧接口)适配成方形插孔(新接口)。
  • 代理:在插头与插座之间加装一个保护套(代理),防止过载。

典型应用场景与代码实践

场景1:网络请求的封装

// 远程代理在Java中的实现
public interface WeatherAPI {
    String getTemperature(String city);
}

// 真实服务(模拟远程调用)
class RealWeatherAPI implements WeatherAPI {
    @Override
    public String getTemperature(String city) {
        // 模拟网络延迟
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
        return "25°C";
    }
}

// 代理添加缓存功能
class CachingWeatherProxy implements WeatherAPI {
    private final Map<String, String> cache = new HashMap<>();
    private final WeatherAPI realAPI;

    public CachingWeatherProxy(WeatherAPI realAPI) {
        this.realAPI = realAPI;
    }

    @Override
    public String getTemperature(String city) {
        if (cache.containsKey(city)) {
            return cache.get(city);
        } else {
            String result = realAPI.getTemperature(city);
            cache.put(city, result);
            return result;
        }
    }
}

场景2:图像加载优化

在移动应用开发中,使用虚拟代理避免一次性加载大量高分辨率图片:

class ImageView {
    constructor(filename) {
        this.filename = filename;
        this.imageProxy = new ImageProxy(filename);
    }

    display() {
        this.imageProxy.show();
    }
}

class ImageProxy {
    constructor(filename) {
        this.filename = filename;
        this.realImage = null;
    }

    show() {
        if (!this.realImage) {
            this.realImage = new RealImage(this.filename);
            // 模拟延迟加载
            setTimeout(() => this.realImage.render(), 1000);
        }
        // 先显示占位图
        document.write('<img src="placeholder.jpg" />');
    }
}

实现代理模式的注意事项

  1. 避免过度设计:不要为每个对象都添加代理,仅在必要时使用(如敏感资源、远程调用)。
  2. 保持接口一致性:代理与真实对象必须实现相同的接口,确保客户端无感知。
  3. 性能权衡:代理会增加一层调用开销,需在控制需求与性能之间找到平衡点。

结论

代理模式如同软件系统中的“智能中间件”,通过间接访问机制解决了权限控制、性能优化和资源管理等复杂问题。无论是远程服务调用、敏感数据访问,还是图像加载优化,代理模式都能以优雅的方式提升系统设计的灵活性与安全性。

掌握代理模式的关键在于理解“间接访问”的核心思想,并在实际开发中根据需求选择合适的代理类型。随着项目复杂度的提升,合理使用代理模式将帮助开发者构建出更健壮、可维护的代码架构。

(全文约 1800 字)

最新发布