代理

设计模式 / 结构型模式
image

//Subject: a pointer to a function plays the virtual draw()
typedef struct Graphic {
    const char *fileName;
    void (*draw)(struct Graphic *self);
} Graphic;

//RealSubject: it is costly to create
typedef struct {
    Graphic base;
} Image;

//Request()
void imageDraw(Graphic *self) {
    printf("draw %s\n", self->fileName);
}

Image *imageNew(const char *fileName) {
    printf("load %s\n", fileName);
    Image *image = malloc(sizeof *image);
    *image = (Image){{fileName, imageDraw}};
    return image;
}

//Proxy: the same Subject, the real image is made on the first draw()
typedef struct {
    Graphic base;
    Image *image;
} ImageProxy;

Image *getImage(ImageProxy *proxy) {
    if (proxy->image == nullptr) {
        proxy->image = imageNew(proxy->base.fileName);
    }
    return proxy->image;
}

void proxyDraw(Graphic *self) {
    Graphic *image = &getImage((ImageProxy *)self)->base;
    image->draw(image);
}

//Client
ImageProxy proxy = {{"1.png", proxyDraw}, nullptr};
Graphic *graphic = &proxy.base;
//operation without creating a RealSubject
const char *fileName = graphic->fileName;
//forwarded to the RealSubject
graphic->draw(graphic);
//printed: load 1.png
//         draw 1.png
graphic->draw(graphic);
//printed: draw 1.png

printf("fileName: %s\n", fileName);
free(proxy.image);


Provide a surrogate or placeholder for another object to control access to it. The proxy implements the interface of the original object.
Read more

问题。一份文档包含许多大图。如果程序在打开文档时就加载所有图片,打开会很慢,尽管用户只看到第一页。显示图片的代码应当保持不变,但加载必须推迟到真正需要某张图片时。

思路。代理代替另一个对象,即真实对象。两者接口相同,因此客户端看不出它们的区别。代理决定何时以及如何使用真实对象。这里的代理属于虚拟代理:它只在第一次请求时创建真实对象,之后把每次调用都转发给它。其他种类的代理会检查访问权限、维护缓存,或者访问另一台计算机上的对象。

如何阅读示例。真实对象是一张创建代价高昂的图像。代理只保存文件名,并为图像留出一个空位。在大多数语言中,客户端先询问文件名,代理自己就能回答。当客户端要求绘制时,代理创建真实的图像,保存下来并转发这次调用。客户端始终只与代理打交道。在某些语言中,输出还会显示真实图像被加载的时刻。

何时使用。要控制对某个对象的访问、又不改变该对象及其客户端时,使用代理模式。典型的任务有:推迟代价高昂的创建、检查权限、统计调用次数。对象创建代价低廉而且总是需要时,不要使用它:多出的一层只会拖慢代码。

常见错误。没有保存已创建的对象。这样代理每次调用都会创建一个新的真实对象,节省也就无从谈起。