代理模式(Proxy Pattern)的核心是为对象提供一个代理,通过代理控制对原对象的访问 ,常用于添加额外逻辑(如权限校验、缓存、远程访问等)而不修改原对象。在C语言中,可以通过结构体封装原对象指针+重写接口方法实现:代理对象实现与原对象相同的接口,内部调用原对象方法并添加控制逻辑。
C语言实现代理模式的思路
- 抽象主题(Subject):定义原对象和代理对象的统一接口(函数指针结构体)。
- 真实主题(Real Subject):实现抽象主题的核心功能(被代理的对象)。
- 代理(Proxy):实现抽象主题接口,内部包含真实主题的指针,在调用真实主题方法前/后添加控制逻辑(如权限检查、日志记录)。
示例:文件操作代理(添加权限校验)
假设需要一个文件读写模块,要求只有管理员权限才能写入文件,普通用户只能读取。通过代理模式在不修改原始文件操作逻辑的前提下添加权限控制。
步骤1:定义抽象主题(文件操作接口)
c
// 抽象主题:文件操作接口
typedef struct FileSubject {
// 读取文件内容
char* (*read)(struct FileSubject* self, const char* filename);
// 写入文件内容
int (*write)(struct FileSubject* self, const char* filename, const char* content);
// 销毁对象
void (*destroy)(struct FileSubject* self);
} FileSubject;
步骤2:实现真实主题(原始文件操作)
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 真实主题:原始文件操作(无权限控制)
typedef struct {
FileSubject subject; // 继承抽象接口
} RealFileOperator;
// 原始读取逻辑:读取文件内容(简单实现)
static char* real_read(FileSubject* self, const char* filename) {
FILE* file = fopen(filename, "r");
if (!file) return NULL;
// 获取文件大小
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
// 分配内存并读取内容
char* content = (char*)malloc(size + 1);
if (content) {
fread(content, 1, size, file);
content[size] = '\0';
}
fclose(file);
return content;
}
// 原始写入逻辑:写入文件内容
static int real_write(FileSubject* self, const char* filename, const char* content) {
FILE* file = fopen(filename, "w");
if (!file) return 0;
fputs(content, file);
fclose(file);
return 1; // 写入成功
}
// 真实主题的销毁
static void real_destroy(FileSubject* self) {
free(self); // 释放真实对象内存
}
// 创建真实文件操作对象
FileSubject* real_file_operator_create() {
RealFileOperator* real = (RealFileOperator*)malloc(sizeof(RealFileOperator));
if (!real) return NULL;
// 绑定接口方法
real->subject.read = real_read;
real->subject.write = real_write;
real->subject.destroy = real_destroy;
return (FileSubject*)real;
}
步骤3:实现代理(添加权限控制)
代理对象包含真实主题的指针,并在read/write方法中添加权限校验逻辑。
c
// 权限类型(用于代理控制)
typedef enum {
USER_GUEST, // 访客(只读)
USER_ADMIN // 管理员(读写)
} UserType;
// 代理:带权限控制的文件操作
typedef struct {
FileSubject subject; // 继承抽象接口
FileSubject* real_subject; // 指向真实主题(被代理对象)
UserType user_type; // 代理的额外属性:用户权限
} FileProxy;
// 代理的读取逻辑:允许所有用户读取
static char* proxy_read(FileSubject* self, const char* filename) {
FileProxy* proxy = (FileProxy*)self;
printf("[代理] 用户<%d>尝试读取文件: %s\n", proxy->user_type, filename);
// 调用真实主题的读取方法
char* content = proxy->real_subject->read(proxy->real_subject, filename);
if (content) {
printf("[代理] 读取成功\n");
} else {
printf("[代理] 读取失败\n");
}
return content;
}
// 代理的写入逻辑:仅管理员可写入
static int proxy_write(FileSubject* self, const char* filename, const char* content) {
FileProxy* proxy = (FileProxy*)self;
printf("[代理] 用户<%d>尝试写入文件: %s\n", proxy->user_type, filename);
// 权限校验(代理的核心控制逻辑)
if (proxy->user_type != USER_ADMIN) {
printf("[代理] 权限不足:仅管理员可写入\n");
return 0; // 写入失败
}
// 权限通过,调用真实主题的写入方法
int result = proxy->real_subject->write(proxy->real_subject, filename, content);
printf("[代理] 写入%s\n", result ? "成功" : "失败");
return result;
}
// 代理的销毁:先销毁真实主题,再释放自身
static void proxy_destroy(FileSubject* self) {
FileProxy* proxy = (FileProxy*)self;
proxy->real_subject->destroy(proxy->real_subject); // 销毁被代理对象
free(proxy); // 释放代理对象
}
// 创建文件代理(绑定真实主题和用户权限)
FileSubject* file_proxy_create(FileSubject* real_subject, UserType user_type) {
if (!real_subject) return NULL;
FileProxy* proxy = (FileProxy*)malloc(sizeof(FileProxy));
if (!proxy) return NULL;
// 绑定接口方法(重写read和write,添加控制逻辑)
proxy->subject.read = proxy_read;
proxy->subject.write = proxy_write;
proxy->subject.destroy = proxy_destroy;
// 关联真实主题和用户权限
proxy->real_subject = real_subject;
proxy->user_type = user_type;
return (FileSubject*)proxy;
}
步骤4:使用代理模式
客户端通过代理对象访问文件,无需直接操作真实主题,权限控制逻辑由代理透明处理。
c
int main() {
// 1. 创建真实文件操作对象(被代理)
FileSubject* real_file_op = real_file_operator_create();
if (!real_file_op) {
printf("创建真实文件操作失败\n");
return 1;
}
// 2. 创建访客代理(权限:只读)
FileSubject* guest_proxy = file_proxy_create(real_file_op, USER_GUEST);
if (!guest_proxy) {
real_file_op->destroy(real_file_op);
return 1;
}
// 访客尝试写入(预期失败)
guest_proxy->write(guest_proxy, "test.txt", "访客写入的内容");
// 访客尝试读取(预期成功)
char* content = guest_proxy->read(guest_proxy, "test.txt");
if (content) {
printf("读取到内容: %s\n", content);
free(content); // 释放读取的内容
}
// 3. 销毁访客代理(会自动销毁真实对象,需重新创建)
guest_proxy->destroy(guest_proxy);
// 重新创建真实对象和管理员代理
real_file_op = real_file_operator_create();
FileSubject* admin_proxy = file_proxy_create(real_file_op, USER_ADMIN);
// 管理员尝试写入(预期成功)
admin_proxy->write(admin_proxy, "test.txt", "管理员写入的内容");
// 管理员尝试读取(预期成功)
content = admin_proxy->read(admin_proxy, "test.txt");
if (content) {
printf("读取到内容: %s\n", content);
free(content);
}
// 销毁管理员代理
admin_proxy->destroy(admin_proxy);
return 0;
}
输出结果
[代理] 用户<0>尝试写入文件: test.txt
[代理] 权限不足:仅管理员可写入
[代理] 用户<0>尝试读取文件: test.txt
[代理] 读取失败
[代理] 用户<1>尝试写入文件: test.txt
[代理] 写入成功
[代理] 用户<1>尝试读取文件: test.txt
[代理] 读取成功
读取到内容: 管理员写入的内容
核心思想总结
- 控制访问:代理通过在调用真实对象方法前添加逻辑(如权限校验),实现对原对象的访问控制,且不修改原对象代码。
- 透明性 :代理与真实对象实现相同的
FileSubject接口,客户端无需区分,可无缝切换。 - 功能扩展:除权限控制外,代理还可添加缓存(重复读取时返回缓存内容)、日志记录、远程访问(代理作为本地对象,实际调用远程服务)等功能。
C语言通过结构体封装真实对象指针和额外属性(如user_type),结合函数指针重写实现了代理模式的核心,适合需要在不侵入原逻辑的前提下增强对象功能的场景。