一、什么是代理模式?

代理模式(Proxy Pattern)是一种结构型设计模式,它为你提供了一个对象的替代品或占位符,以便控制对原始对象的访问。

二、代理模式的核心组件

  1. 主题 (Subject):一个接口,定义了真实对象和代理对象的共同行为。客户端通过这个接口与它们交互。
  2. 真实主题 (Real Subject):实现了主题接口的类,是代理所代表的真实对象。它包含了核心的业务逻辑。
  3. 代理 (Proxy):同样实现了主题接口。它内部维护一个对真实主题对象的引用,并且可以在调用真实主题的方法前后执行额外的逻辑。

三、示例:下载器的实现

3.1 基础结构

首先,定义我们的Subject接口和Real Subject类。


// Subject: 文件下载器的接口 interface IDownloader { download(url: string): Promise<string>; } // Real Subject: 真实的文件下载器,模拟一个耗时的操作 class RealFileDownloader implements IDownloader { constructor() { // 模拟昂贵的构造过程 console.log('RealFileDownloader: 正在初始化,连接服务器...'); // 假设这里有复杂的初始化逻辑 } public async download(url: string): Promise<string> { console.log(`RealFileDownloader: 开始从 ${url} 下载文件...`); // 模拟网络延迟 await new Promise((resolve) => setTimeout(resolve, 2000)); const fileContent = `这是从 ${url} 下载的文件内容`; console.log('RealFileDownloader: 文件下载完成。'); return fileContent; } }

3.2 虚拟代理 (Virtual Proxy) 实现懒加载

我们不希望一创建下载器实例就开始初始化。只有当用户真正调用 download 方法时,才去创建那个RealFileDownloader


// Proxy: 虚拟代理,实现懒加载 class LazyDownloaderProxy implements IDownloader { private realDownloader: RealFileDownloader | null = null; private url: string; constructor(url: string) { this.url = url; console.log('LazyDownloaderProxy: 实例已创建,但真实下载器尚未初始化。'); } public download(): Promise<string> { // 只有在第一次调用 download 时,才真正创建 RealFileDownloader if (this.realDownloader === null) { console.log('LazyDownloaderProxy: 检测到首次下载请求,开始初始化真实下载器。'); this.realDownloader = new RealFileDownloader(); } return this.realDownloader.download(this.url); } } // --- 使用 --- async function startDown() { console.log('--- 懒加载测试 ---'); const lazyProxy = new LazyDownloaderProxy('https://example.com/bigfile.zip'); console.log('代理已创建,现在执行其他操作...'); // ... (可以执行很多其他代码,RealFileDownloader 还未被创建) console.log('准备调用下载...'); await lazyProxy.download(); } startDown();

结果分析:你会看到 RealFileDownloader 的初始化日志是在调用 download() 方法时才打印出来的,完美实现了懒加载,节省了资源。

3.3 保护代理 (Protection Proxy) 实现权限控制

假设只有管理员才能下载文件。我们可以在代理中加入权限检查。


// Proxy: 保护代理,实现访问控制 class ProtectedDownloaderProxy implements IDownloader { private realDownloader: RealFileDownloader; private userRole: 'admin' | 'guest'; constructor(userRole: 'admin' | 'guest') { this.realDownloader = new RealFileDownloader(); // 这里为了演示,直接创建 this.userRole = userRole; } public download(url: string): Promise<string> { if (this.userRole === 'admin') { console.log('ProtectedDownloaderProxy: 权限验证通过!'); return this.realDownloader.download(url); } else { console.error('ProtectedDownloaderProxy: 权限不足,禁止下载!'); return Promise.reject(new Error('Access Denied')); } } } // --- 使用 --- async function startDown() { console.log('\n--- 权限控制测试 ---'); const adminProxy = new ProtectedDownloaderProxy('admin'); await adminProxy.download('https://example.com/secret.doc'); try { const guestProxy = new ProtectedDownloaderProxy('guest'); await guestProxy.download('https://example.com/secret.doc'); } catch (error) { console.log(`捕获到错误: ${(error as Error).message}`); } } startDown();

结果分析:管理员用户可以成功下载,而访客用户则会被代理拦截并抛出错误,RealFileDownloader 的 download 方法根本不会被执行。

3.4 缓存代理 (Caching Proxy)

对于相同的URL,我们不应该重复下载。代理可以轻松地加入缓存逻辑。


// Proxy: 缓存代理 class CachingDownloaderProxy implements IDownloader { private realDownloader: RealFileDownloader; private cache: Map<string, string> = new Map(); constructor() { this.realDownloader = new RealFileDownloader(); } public async download(url: string): Promise<string> { if (this.cache.has(url)) { console.log(`CachingDownloaderProxy: 命中缓存,直接返回 ${url} 的内容。`); return this.cache.get(url)!; } console.log(`CachingDownloaderProxy: 未命中缓存,请求真实下载器...`); const fileContent = await this.realDownloader.download(url); this.cache.set(url, fileContent); // 下载后存入缓存 return fileContent; } } // --- 使用 --- async function startDown() { // --- 使用 --- console.log('\n--- 缓存测试 ---'); const cacheProxy = new CachingDownloaderProxy(); console.log('第一次下载:'); await cacheProxy.download('https://example.com/logo.png'); console.log('\n第二次下载同一文件:'); await cacheProxy.download('https://example.com/logo.png'); } startDown();

结果分析:第二次调用 download 时,你会看到它立即从缓存返回,而没有触发真实下载器的耗时操作。

为了方便大家学习和实践,本文的所有示例代码和完整项目结构都已整理上传至我的 GitHub 仓库。欢迎大家克隆、研究、提出 Issue,共同进步!

更多推荐