[学习笔记]设计模式之Proxy
为方便读者,本文已添加至索引:
写在前面
“魔镜啊魔镜,谁是这个世界上最美丽的人?” 每到晚上,女王都会问魔镜相同的问题(见Decorator模式)。这是她还曾身为女巫时留下的习惯。尽管要说起这个内心邪恶的女巫,将会有一大堆故事,但我们今天要讨论的主角,却是这面神奇的镜子。关于魔镜的来历,谁都不是很清楚。就连这个世界的创造者魔导士(见Builder模式)也对它的存在感到好奇。魔镜能够回应主人的诉求,回答主人所提出的问题,并透过镜子来提示答案相关的信息。我们可以通过时の魔导士的研究手札来依稀了解下这个神秘的魔法造物。
“这面魔镜可以显现出我所创造的这个世界中任何的物体,这很有趣。”
“……但是它似乎也仅仅是提供了一种受限制的访问对象的方式,因为我不能通过它直接接触到对象本身。”
“……它也仅在我需要的时候才会显示出对应的物体,我相信它并非从诞生之时就存储好了世界上所有物体的影像。”
“……魔镜在显示答案时更像是一种采用了Proxy模式的造物。”
是的,他提到了Proxy(代理),一种设计模式。在继续深入研究魔镜之前,我们先来了解下Proxy的大致内容。
要点梳理
- 目的分类
- 对象结构型模式
- 范围准则
- 对象(该模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性)
- 主要功能
- 为其他对象提供一种代理以控制对这个对象的访问
- 适用情况
- 在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy。
- 远程代理(Remote Proxy):为一个对象在不同的地址空间提供局部代表
- 虚代理(Virtual Proxy):根据需要创建开销很大的对象
- 保护代理(Protection Proxy):控制对原始对象的访问
- 智能指引(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作。比如:对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它;在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它等等。
- 参与部分
- Proxy:保存一个引用使得代理可以访问实体;提供一个与Subject的接口相同的接口,用来替代实体;控制对实体的存取,并可能负责创建和删除它;
- Subject:定义RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy
- RealSubject:定义Proxy所代表的实体
- 协作过程
- 代理根据其种类,在适当的时候向RealSubject转发请求。
- UML图例
示例分析 - 无所不知的魔镜
为了更加深入地了解魔镜,我们先回顾一个知识点:在Decorator模式笔记中,我们了解到一个所有可见物体的抽象类VisualObject。从镜子之中,我们可以看到某个对象的影像Image:
1 class Image : public VisualObject { 2 public: 3 Image(string name); // load an image. 4 virtual ~Image(); 5 6 virtual void show(); 7 }
然而透过魔镜,我们可以看到任何想看东西的Image。如前文所述,不可能让魔镜在一开始就把所有的Image都实例化。(换成我们程序员的思维,就是会造成存储开销过于巨大),怎么办?Proxy模式给出了一种解决策略。让我们看看所谓的ImageProxyMagic吧:
1 class ImageProxyMagic : public VisualObject { 2 public: 3 ImageProxyMagic(string name); // Just save the name. 4 virtual ~Image(); 5 6 virtual void show(); 7 protected: 8 Image* getImage(); 9 private: 10 string _name; 11 Image* _image; 12 }
诶,有没有发现它对外的接口和Image相同?是的,这样一来我们就可以像操作Image一样,操作ImageProxyMagic了。但是具体它又做了什么?看看代码吧:
1 ImageProxyMagic::ImageProxyMagic(string name) { 2 _name = name; 3 _image = 0; 4 } 5 6 Image* ImageProxyMagic::getImage() { 7 if (!_image) { 8 _image = new Image(_name); 9 } 10 return _image; 11 } 12 13 void ImageProxyMagic::show() { 14 getImage()->show(); 15 }
注意到,构造函数存储了Image的名字,而将Image的装载过程延缓到getImage函数当中。因而,只有在某个Image真正需要show出来的时候,它才会被装载。ImageProxyMagic将show命令转发给Image处理。
尽管如此,我们还是不了解魔镜为什么会知道问题的答案,我们仅仅看到的它在展示答案时候的一个可能处理方式。我们不禁想对它提出这样一个问题:
“魔镜啊魔镜,你为什么无所不知?”
特点总结
使用Proxy模式在访问对象时引入了一定程度的间接性。根据代理的类型,附加的间接性也有多种用途:
- Remote Proxy可以隐藏一个对象存在于不同地址空间的事实。
- Virtual Proxy可以进行最优化,例如根据要求创建对象。
- Protectoin Proxy和Smart Reference都允许在访问一个对象时有一些附加的内务处理。
Proxy模式并不总是需要知道实体的类型。如果Proxy类能够完全通过一个抽象接口处理它的实体,则无须为每一个RealSubject类都生成一个Proxy类;但如果Proxy要实例化RealSubject的话,(比如我们的例子中)那它必须知道具体的类。
写在最后
今天的笔记就到这里了,欢迎大家批评指正!如果觉得可以的话,好文推荐一下,我会非常感谢的!