大话设计模式C语言之代理模式
代理模式
代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
代理模式和上篇提到的同为结构型设计模式的装饰模式很像,尤其是用C实现结构几乎一致,但语义完全不同,这也是 C 里最容易被滥用的地方。为了阅读下面具体例子时不会混淆,这里先说下两者的本质区别,就是两者的核心目的不同:
- 上篇学习的装饰模式主要是为了给对象增加职责。在不改变接口的前提下叠加功能。
- 而代理模式主要是为了控制对对象的访问,决定能不能/何时/是否调用真实对象,下面结合具体例子很容易就可以理解。
案例代码
这里与原书不同举了一个简单的下载的例子,以便更好的体现代理可以控制对真实对象的访问,结合书中例子可以更好理解代理模式。
服务接口 download_service.h
服务接口(Service Interface)声明了服务接口。代理必须遵循该接口才能伪装成服务对象。
该类定义了真实对象类和代理类的公用接口,这样就可以在任何使用真实对象的地方使用代理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #ifndef DOWNLOAD_SERVICE_H #define DOWNLOAD_SERVICE_H
typedef struct DownloadService DownloadService;
struct DownloadService { void (*download)(DownloadService *self, const char *url); void (*destroy)(DownloadService *self); };
#endif
|
真正服务类 real_download.c
服务(Service)类定义了proxy所代表的真实实体,提供了一些实用的业务逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <stdio.h> #include <stdlib.h> #include "download_service.h"
typedef struct { DownloadService base; } RealDownloadService;
static void real_download(DownloadService *self, const char *url) { (void)self; printf("[Real] Downloading file from: %s\n", url); }
static void real_destroy(DownloadService *self) { free(self); }
DownloadService *real_download_service_create(void) { RealDownloadService *service = malloc(sizeof(RealDownloadService)); if (!service) return NULL;
service->base.download = real_download; service->base.destroy = real_destroy;
return (DownloadService *)service; }
|
代理类 proxy_download.c
代理(Proxy)类包含一个指向服务对象的引用成员变量。代理完成其任务(例如延迟初始化、记录日志、访问控制和缓存等)后会将请求传递给服务对象。通常情况下,代理会对其服务对象的整个生命周期进行管理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include "download_service.h"
typedef struct { DownloadService base; DownloadService *real_service; int has_permission; } DownloadServiceProxy;
static int check_permission(DownloadServiceProxy *proxy) { return proxy->has_permission; }
static void proxy_download(DownloadService *self, const char *url) { DownloadServiceProxy *proxy = (DownloadServiceProxy *)self;
printf("[Proxy] Request download: %s\n", url);
if (!check_permission(proxy)) { printf("[Proxy] Permission denied.\n"); return; }
proxy->real_service->download(proxy->real_service, url); }
static void proxy_destroy(DownloadService *self) { DownloadServiceProxy *proxy = (DownloadServiceProxy *)self;
if (proxy->real_service) { proxy->real_service->destroy(proxy->real_service); }
free(proxy); }
DownloadService *download_service_proxy_create( DownloadService *real_service, int has_permission) { DownloadServiceProxy *proxy = malloc(sizeof(DownloadServiceProxy)); if (!proxy) return NULL;
proxy->base.download = proxy_download; proxy->base.destroy = proxy_destroy; proxy->real_service = real_service; proxy->has_permission = has_permission;
return (DownloadService *)proxy; }
|
测试代码 main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <stdio.h> #include "download_service.h"
DownloadService *real_download_service_create(void); DownloadService *download_service_proxy_create( DownloadService *real_service, int has_permission);
int main(void) {
int permission = 0; DownloadService *real = real_download_service_create(); printf("Enter 1 enable download permission, 0 disable download permission\n"); scanf("%d", &permission);
DownloadService *proxy = download_service_proxy_create(real, permission);
proxy->download(proxy, "https://example.com/file.zip");
proxy->destroy(proxy);
while (1); return 0; }
|
总结
代理和装饰有着相似的结构,但是其意图却非常不同。这两个模式的构建都基于组合原则,也就是说一个对象应该将部分工作委派给另一个对象。看完上面具体例子后在这里再详细说下两者更多的区别。
|
代理模式 |
装饰模式 |
| 从结构上 |
代理模式通常只包一层 |
装饰模式通常都是多层嵌套 |
| 真实对象 |
代理可以不调用真实对象 |
每一层都会调用被装饰对象 |
| 生命周期 |
通常自行管理其服务对象的生命周期 |
装饰器通常伴随其装饰组件对象释放 |