用C语言实现代理模式
代理模式(Proxy Pattern)的核心是为对象提供一个代理,通过代理控制对原对象的访问,常用于添加额外逻辑(如权限校验、缓存、远程访问等)而不修改原对象。在C语言中,可以通过结构体封装原对象指针+重写接口方法实现:代理对象实现与原对象相同的接口,内部调用原对象方法并添加控制逻辑。
C语言实现代理模式的思路
- 抽象主题(Subject):定义原对象和代理对象的统一接口(函数指针结构体)。
- 真实主题(Real Subject):实现抽象主题的核心功能(被代理的对象)。
- 代理(Proxy):实现抽象主题接口,内部包含真实主题的指针,在调用真实主题方法前/后添加控制逻辑(如权限检查、日志记录)。
示例:文件操作代理(添加权限校验)
假设需要一个文件读写模块,要求只有管理员权限才能写入文件,普通用户只能读取。通过代理模式在不修改原始文件操作逻辑的前提下添加权限控制。
步骤1:定义抽象主题(文件操作接口)
// 抽象主题:文件操作接口
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:实现真实主题(原始文件操作)
#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
方法中添加权限校验逻辑。
// 权限类型(用于代理控制)
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:使用代理模式
客户端通过代理对象访问文件,无需直接操作真实主题,权限控制逻辑由代理透明处理。
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
),结合函数指针重写实现了代理模式的核心,适合需要在不侵入原逻辑的前提下增强对象功能的场景。