一、模式定义
代理模式使用代理对象完成用户请求,屏蔽用户对真实对象的访问。通过代理对象访问目标对象,这样做的好处就是可以在目标对象实现的基础上,扩展目标对象的功能。代理模式分为静态代理和动态代理。
二、模式作用
控制和管理对目标对象的访问
三、模式的角色和UML图
抽象角色:声明真实对象和代理对象的共同接口
代理角色:
1.代理对象角色内部含有对真实对象的引用,从而可以操作真实对象
2.代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象
3.代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象
#include<iostream>
using namespace std;
/* 抽象角色 */
class Subject {
public:
virtual void request() = 0;
};
/* 真实的角色 */
class RealSubject :public Subject {
public:
void request() override {
cout << "RealSubject: request" << endl;
}
};
/* 静态代理,对具体真实对象直接引用
* 代理角色,代理角色需要有对真实角色的引用,代理做真实角色想做的事情
*/
class ProxySubject :public Subject {
public:
//除了代理真实角色做该做的事情,代理角色也可以提供附加操作,
//如:preRequest()和postRequest()
void request() override {
preRequest();
if (realSubject == NULL) {
realSubject = new RealSubject(); // 延迟加载:只有在调用方法的时候,才会去new对象
}
realSubject->request();
postRequest();
}
void preRequest() { }//真实角色操作前的附加操作
void postRequest() { }//真实角色操作后的附加操作
private:
RealSubject *realSubject = NULL;
};
int main() {
Subject *subject = new ProxySubject();
subject->request(); //代理者代替真实者做事情
return 0;
}
以卖房(Subject的request方法)为例,卖房者(RealSubject)想卖房,但是不想搞那么多麻烦事,不想天天招买房的骚扰,就委托中介(ProxySubject)去帮忙卖房,中介帮忙卖房,当然不会简单的卖房,可能还需要收取中介费(preRequest()和postRequest()方法)等等附加收费,最终买房者(客户端Main)买房,找到的是中介,不会与房主接触,与房主接触的是中介,买房者最终跟中介买房,完成操作。
四、应用场景
1.远程代理:对一个位于不同的地址空间对象提供一个局域代表对象。
2.虚拟代理:根据需要将一个资源消耗很大或者比较复杂的对象,延迟加载,在真正 需要的时候才创建;在真实对象创建成功之前虚拟代理 扮演真实对象的替身,而当 真实对象创建之后,虚拟代理将用户的请求转发给真实对象。
3.保护代理:控制对目标对象的访问,给不同的用户提供不同的访问权限即提供不同的操作函数。
4.Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
远程代理实例:
我们在国内因为GFW,所以不能访问 facebook,我们可以用翻墙(设置代理)的方法访问。访问过程是:
1.用户把HTTP请求发给代理
2.代理把HTTP请求发给web服务器
3.web服务器把HTTP响应发给代理
4.代理把HTTP响应发回给用户
虚拟代理实例:
考虑一个可以在文档中嵌入图形对象的文档编辑器。有些图形对象的创建开销很大。但是打开文档必须很迅速,因此我们在打开文档时应避免一次性创建所有开销很大的对象。这里就可以运用代理模式,在打开文档时,并不打开图形对象,而是打开图形对象的代理以替代真实的图形。待到真正需要打开图形时,仍由代理负责打开。
class CImage
8 {
9 public:
10 virtual ~CImage(){cout << "~CImage()\n";}
11 virtual void Show(){}
12 protected:
13 CImage(string imgName): m_imgName(imgName){cout << "CImage()\n";}
14 string m_imgName;
15 };
16
17 //concrete implement
18 class CBigImage : public CImage
19 {
20 public:
21 CBigImage(string imgName): CImage(imgName), m_bigImgName(imgName){
22 cout << "CBigImage()\n";
23 }
24 ~CBigImage(){
25 cout << "~CBigImage()\n";
26 }
27 void Show(){cout << "show big image" <<endl;}
28 protected:
29 string m_bigImgName;
30 };
31
32 // proxy
33 class CBigImageProxy : public CImage
34 {
35 public:
36 CBigImageProxy(string imgName): CImage(imgName), m_img(NULL), m_bigImgName(imgName){
37 cout << "CBigImageProxy()\n";
38 }
39 ~CBigImageProxy(){
40 if(NULL != m_img){
41 delete m_img;
42 m_img = NULL;
43 }
44 cout << "~CBigImageProxy()\n";
45 }
46 void Show(){
47 if(NULL == m_img){
48 m_img = new CBigImage(m_bigImgName);
49 }
50 m_img->Show();
51 }
52 private:
53 CImage *m_img;
54 string m_bigImgName;
55 };
五、静态代理和动态代理
静态代理
真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
动态代理
通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系
注:本文章为学习总结笔记,在各位牛人的基础之上借鉴总结而成。