概述
因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路径) ? 你有想过限制访问某个对象,也就是说,提供一组方法给普通用户,特别方法给管理员用户?以上两种需求都非常类似,并且都需要解决一个更大的问题:你如何提供一致的接口给某个对象让它可以改变其内部功能,或者是从来不存在的功能? 可以通过引入一个新的对象,来实现对真实对象的操作或者将新的对象作为真实对象的一个替身。即代理对象。它可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务。
例子1:经典例子就是网络代理,你想访问facebook或者twitter ,如何绕过GFW,找个代理网站。
例子2:可以调用远程代理处理一些操作如图:
2.问题:
你怎样才能在不直接操作对象的情况下,对此对象进行访问?
3.解决方案
代理模式: 为其他对象提供一种代理,并以控制对这个对象的访问。(Provide asurrogate or placeholderforanother object tocontrol access to it. )而对一个对象进行访问控制的一个原因是为了只有在我们确实需要这个对象时才对它进行创建和初始化。它是给某一个对象提供一个替代者(占位者),使之在client对象和subject对象之间编码更有效率。代理可以提供延迟实例化(lazy instantiation),控制访问, 等等,包括只在调用中传递。 一个处理纯本地资源的代理有时被称作虚拟代理。远程服务的代理常常称为远程代理。强制 控制访问的代理称为保护代理。
4.实用性
在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用 Proxy模式。下面是一些可以使用Proxy模式常见情况:
1) 远程代理(Remote Proxy)为一个位于不同的地址空间的对象提供一个本地的代理对象。这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又叫做大使(Ambassador)
2) 虚拟代理(Virtual Proxy)根据需要创建开销很大的对象。如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
3) 保护代理(Protection Proxy)控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
4) 智能指引(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。
5) Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
5. 结构
Uml图:
简单结构示意图:
与其他相关模式
1)适配器模式Adapter:适配器Adapter 为它所适配的对象提供了一个不同的接口。相反,代理提供了与它的实体相同的接口。然而,用于访问保护的代理可能会拒绝执行实体会执行的操作,因此,它的接口实际上可能只是实体接口的一个子集。
2) 装饰器模式Decorator:尽管Decorator的实现部分与代理相似,但 Decorator的目的不一样。Decorator为对象添加一个或多个功能,而代理则控制对对象的访问。
总结
代理模式在很多情况下都非常有用,特别是你想强行控制一个对象的时候,比如:延迟加载,监视状态变更的方法等等
1、“增加一层间接层”是软件系统中对许多负责问题的一种常见解决方法。在面向对象系统中,直接使用某些对象会带来很多问题,作为间接层的proxy对象便是解决这一问题的常用手段。
2、具体proxy设计模式的实现方法、实现粒度都相差很大,有些可能对单个对象作细粒度的控制,有些可能对组件模块提供抽象代理层,在架构层次对对象作proxy。
3、proxy并不一定要求保持接口的一致性,只要能够实现间接控制,有时候损及一些透明性是可以接受的。例如上面的
注意:下面代码存在错误,基类的析构函数必须声明为virtual
12.4 代理模式的扩展
12.4.1 普通代理
在网络上代理服务器设置分为透明代理和普通代理,是什么意思呢?透明代理就是用户不用设置代理服务器地址,就可以直接访问,也就是说代理服务器对用户来说透明的,看不到,不用知道它存在的;普通代理则是需要用户自己设置代理服务器的IP地址,用户必须知道代理的存在。我们设计模式中的普通代理和强制代理也是类似的一种结构,普通代理就是我们要知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问;强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的,这样解释还是比较复杂,我们还是用实例来讲解。
首先说普通代理,它的要求就是客户端只能访问代理角色,而不能访问真实角色,这是比较简单的,我们以上面的例子作为扩展,我自己作为一个游戏玩家,我肯定自己不练级了,也就是场景类不能再直接new一个GamePlayer对象了,它必须由GampePlayerProxy来进行模拟场景,类图修改如图12-4所示。
#include <string> #include <iostream> using namespace std; /// 游戏接口类 class IGamePlayer { public: virtual void login(string user, string password) = 0; virtual void killBoss() = 0; virtual void upgrade() = 0; }; /// 游戏者 class GamePlayer : public IGamePlayer { string _name; ///< 游戏者名称 public: GamePlayer(string name):_name(name){} virtual void login(string user, string password) { cout<<"登录名为"<<user<<" 的用户 " <<_name<<"登录成功!"<<endl; } virtual void killBoss() { cout<<_name<<"在打怪"<<endl; } virtual void upgrade() { cout<<_name<<" 又升了一级!"<<endl; } }; /// 代理 class GamePlayPorxy : public IGamePlayer { IGamePlayer* _pGamePlayer; ///< 代理游戏者 public: GamePlayPorxy(string name) { _pGamePlayer = new GamePlayer(name); } ~GamePlayPorxy() { delete _pGamePlayer; } virtual void login(string user, string password) { _pGamePlayer->login(user, password); } virtual void killBoss() { _pGamePlayer->killBoss(); } virtual void upgrade() { _pGamePlayer->upgrade(); } }; void main() { IGamePlayer* pPorxy = new GamePlayPorxy("张三"); pPorxy->login("butterfly", "123"); pPorxy->killBoss(); pPorxy->upgrade(); delete pPorxy; }
在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色爱怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。当然,在实际的项目中,一般都是通过约定来禁止new一个真实的角色,也是一个非常好的方案。
强制代理
强制代理在设计模式中比较另类,为什么这么说呢?一般的思维都是通过代理找到真实的角色,但是强制代理却是要“强制”,你必须通过真实角色查找到代理角色,否则你不能访问,甭管你是通过代理类还是通过直接new一个主题角色类,都不能访问,只有通过真实角色指定的代理类才可以访问,也就是说由真实角色管理代理角色,这么说吧,高层模块new了一个真实角色的对象,返回的却是代理角色,这就好比是你和一个明星比较熟,相互认识,有件事情你需要向她确认一下,于是你就直接拨通了明星的电话:
“喂,沙比呀,我要见一下XXX导演,你帮下忙了!”
“不行呀衰哥,我这几天很忙呀,你找我的经纪人吧…”
郁闷了吧,你是想直接绕过她的代理,谁知道返回的还是她的代理,这就是强制代理,你可以不用知道代理存在,但是你的所作所为还是需要代理为你提供。我们把上面的例子稍作修改就可以完成,如图12-5所示。
强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色,高层模块只要调用getProxy就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。#include <string> #include <iostream> using namespace std; /// 游戏接口类 class IGamePlayer { public: virtual void login(string user, string password) = 0; virtual void killBoss() = 0; virtual void upgrade() = 0; virtual IGamePlayer* getPorxy() = 0; }; /// 代理 class GamePlayPorxy : public IGamePlayer { IGamePlayer* _pGamePlayer; ///< 代理游戏者 public: GamePlayPorxy(IGamePlayer* pGamePlayer) { _pGamePlayer = pGamePlayer; } ~GamePlayPorxy() { //delete _pGamePlayer; } virtual void login(string user, string password) { _pGamePlayer->login(user, password); } virtual void killBoss() { _pGamePlayer->killBoss(); } virtual void upgrade() { _pGamePlayer->upgrade(); } virtual IGamePlayer* getPorxy() { return this; } }; /// 游戏者 class GamePlayer : public IGamePlayer { string _name; ///< 游戏者名称 GamePlayPorxy* _pPorxy; public: GamePlayer(string name): _name(name), _pPorxy(NULL) { } ~GamePlayer() { if (_pPorxy) { delete _pPorxy; } } virtual void login(string user, string password) { if (_pPorxy) { cout<<"登录名为"<<user<<" 的用户 " <<_name<<"登录成功!"<<endl; } else { cout<<"请使用指定的代理访问"<<endl; } } virtual void killBoss() { if (_pPorxy) { cout<<_name<<"在打怪"<<endl; } else { cout<<"请使用指定的代理访问"<<endl; } } virtual void upgrade() { if (_pPorxy) { cout<<_name<<" 又升了一级!"<<endl; } else { cout<<"请使用指定的代理访问"<<endl; } } virtual IGamePlayer* getPorxy() { _pPorxy = new GamePlayPorxy(this); return _pPorxy; } }; void main() { //错误方式 // IGamePlayer* pGamePlayer = new GamePlayer("张三"); // pGamePlayer->login("butterfly", "123"); // pGamePlayer->killBoss(); // pGamePlayer->upgrade(); // delete pGamePlayer; // 错误方式 // IGamePlayer* pGamePlayer = new GamePlayer("张三"); // IGamePlayer* pPorxy = new GamePlayPorxy(pGamePlayer); // pPorxy->login("butterfly", "123"); // pPorxy->killBoss(); // pPorxy->upgrade(); // delete pGamePlayer; // delete pPorxy; //正确方式 IGamePlayer* pGamePlayer = new GamePlayer("张三"); pGamePlayer->getPorxy()->login("butterfly", "123"); pGamePlayer->getPorxy()->killBoss(); pGamePlayer->getPorxy()->upgrade(); delete pGamePlayer; }
虚拟代理
虚拟代理(Virual Proxy)听着很复杂,其实非常简单,我们只要把代理模式的通用代码稍微修改一下就成为虚拟代理,修改后的代理类如代码清单12-21所示。
#include <string> #include <iostream> using namespace std; /// 游戏接口类 class IGamePlayer { public: virtual void login(string user, string password) = 0; virtual void killBoss() = 0; virtual void upgrade() = 0; }; /// 游戏者 class GamePlayer : public IGamePlayer { string _name; ///< 游戏者名称 public: GamePlayer(string name):_name(name){} virtual void login(string user, string password) { cout<<"登录名为"<<user<<" 的用户 " <<_name<<"登录成功!"<<endl; } virtual void killBoss() { cout<<_name<<"在打怪"<<endl; } virtual void upgrade() { cout<<_name<<" 又升了一级!"<<endl; } }; /// 代理 class GamePlayPorxy : public IGamePlayer { IGamePlayer* _pGamePlayer; ///< 代理游戏者 string _name; public: GamePlayPorxy(string name) :_name(name), _pGamePlayer(NULL) { //_pGamePlayer = new GamePlayer(name); } ~GamePlayPorxy() { delete _pGamePlayer; } virtual void login(string user, string password) { if (!_pGamePlayer) { _pGamePlayer = new GamePlayer(_name); } _pGamePlayer->login(user, password); } virtual void killBoss() { if (!_pGamePlayer) { _pGamePlayer = new GamePlayer(_name); } _pGamePlayer->killBoss(); } virtual void upgrade() { if (!_pGamePlayer) { _pGamePlayer = new GamePlayer(_name); } _pGamePlayer->upgrade(); } }; void main() { IGamePlayer* pPorxy = new GamePlayPorxy("张三"); pPorxy->login("butterfly", "123"); pPorxy->killBoss(); pPorxy->upgrade(); delete pPorxy; }
在需要的时候才初始化主题对象,可以避免被代理对象较多而引起的初始化缓慢的问题,它的缺点就是需要在每个方法中判断主题对象是否被创建,这就是虚拟代理,非常简单。
12.4.5 动态代理
放在最后讲的一般都是压轴大戏,动态代理就是如此,上面的章节都是一个引子,动态代理才是重头戏。嘛是动态代理?动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理那一个对象,相对的来说,自己写代理类的方式就是静态代理。本章节的核心部分就在动态代理上,现在有一个非常流行的名称叫做:面向横切面编程,也就是AOP(Aspect Oriented Programming),其核心就是采用了动态代理机制,既然这么重要,我们就来看看动态代理是如何实现的,还是以打游戏为例,类图修改一下以实现动态代理,如图12-7所示。
图12-7 动态代理
C++实现AOP编程具体可以学习Aspect C++
转自:http://www.cnblogs.com/cbf4life/archive/2010/01/27/1657438.html