代理模式¶
定义¶
代理模式(Proxy Pattern)是一种结构型设计模式,它为另一个对象提供一个替身或占位符以控制对这个对象的访问。
代理对象与真实对象实现相同的接口,因此对调用方完全透明:调用方只和代理打交道,不知道(也不关心)背后是真实对象还是代理。
别名:Surrogate(替身)。
意图¶
- 通过引入一个间接层,控制对真实对象的访问时机、权限、开销等。
- 在不修改真实对象代码的前提下,为其增加额外职责(如缓存、鉴权、日志、懒加载)。
结构¶
classDiagram
class Subject {
<<interface>>
+request()
}
class RealSubject {
+request()
}
class Proxy {
-realSubject: RealSubject
+request()
}
Subject <|.. RealSubject
Subject <|.. Proxy
Proxy o--> RealSubject - Subject(抽象主题):定义真实对象与代理共用的接口。
- RealSubject(真实主题):真正执行业务逻辑的对象。
- Proxy(代理):持有对 RealSubject 的引用,在转发调用前后做控制,并实现与 Subject 相同的接口。
常见分类¶
| 类型 | 用途 |
|---|---|
| 远程代理(Remote Proxy) | 隐藏对象位于不同地址空间的事实,如 RPC、网络接口 |
| 虚拟代理(Virtual Proxy) | 延迟创建开销大的对象,真正使用时才加载 |
| 保护代理(Protection Proxy) | 控制访问权限,调用前校验身份 |
| 缓存代理(Cache Proxy) | 缓存结果,避免重复计算或重复请求 |
| 日志代理(Logging Proxy) | 记录调用日志,便于调试与审计 |
适用场景¶
- 需要对现有对象增加切面式的控制(访问控制、缓存、日志),又不希望侵入真实对象代码。
- 对象创建或访问代价高昂,希望延迟加载(懒加载)。
- 客户端与真实对象不在同一进程/设备,需要隐藏通信细节(嵌入式开发中的驱动访问、远程资源读取)。
代码示例¶
以“保护代理 + 缓存代理”为例:SENSOR_IF 是抽象主题,temperature_sensor 是真实传感器,sensor_proxy 在调用前校验权限并缓存读数,避免频繁读取硬件。
#include <stdio.h>
/* Subject:抽象主题 */
typedef struct sensor_if sensor_if_t;
struct sensor_if {
float (*read_temperature)(const sensor_if_t *self);
};
/* RealSubject:真实传感器(底层硬件读取) */
typedef struct {
sensor_if_t iface;
const char *name;
} temperature_sensor_t;
static float temperature_sensor_read(const sensor_if_t *self)
{
const temperature_sensor_t *s =
(const temperature_sensor_t *)self;
printf("[%s] read from hardware\n", s->name);
return 25.5f;
}
/* Proxy:代理(保护 + 缓存) */
typedef struct {
sensor_if_t iface;
const temperature_sensor_t *target;
int authenticated;
float cached_value;
int cache_valid;
} sensor_proxy_t;
static float sensor_proxy_read(const sensor_if_t *self)
{
const sensor_proxy_t *p = (const sensor_proxy_t *)self;
if (!p->authenticated) {
printf("access denied\n");
return 0.0f;
}
if (p->cache_valid) { /* 缓存命中 */
printf("cache hit: %.1f\n", p->cached_value);
return p->cached_value;
}
float value = p->target->iface.read_temperature(&p->target->iface);
((sensor_proxy_t *)p)->cached_value = value; /* 写入缓存 */
((sensor_proxy_t *)p)->cache_valid = 1;
return value;
}
int main(void)
{
static temperature_sensor_t sensor = {
.iface = { .read_temperature = temperature_sensor_read },
.name = "sensor-1",
};
static sensor_proxy_t proxy = {
.iface = { .read_temperature = sensor_proxy_read },
.target = &sensor,
.authenticated = 0,
};
sensor_if_t *p = (sensor_if_t *)&proxy;
p->read_temperature(p); /* 未授权 -> access denied */
((sensor_proxy_t *)p)->authenticated = 1;
p->read_temperature(p); /* 首次读取硬件 */
p->read_temperature(p); /* 命中缓存 */
return 0;
}
优缺点¶
优点
- 职责清晰:真实对象只关注业务,控制逻辑收敛在代理中。
- 无需修改真实对象,符合开闭原则。
- 可在客户端与真实对象之间引入多种控制(权限、缓存、日志、远程)。
缺点
- 增加一层间接调用,带来少量额外开销。
- 若代理设计不当,可能引入额外复杂性或带来不一致性(如缓存过期)。
与相关模式的区别¶
- 装饰器(Decorator):装饰器动态添加行为,通常递归组合多个装饰层;代理控制访问,一般只包一层。
- 适配器(Adapter):适配器改变接口以匹配客户端;代理保持接口不变,只控制访问。
参考¶
- 四人组《设计模式:可复用面向对象软件的基础》之 Proxy。
- 《Head First 设计模式》远程代理、虚拟代理章节。