跳转至

代理模式

定义

代理模式(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 设计模式》远程代理、虚拟代理章节。