代理模式(结构型)

前言


在一个陌生的城市找房子住的时候,一般都需要通过房产中介或者熟人介绍的方式来找房子。这个第三方的角色相当于代理的角色,负责代理真实的房东或业主来给我们提供相关的租房信息,而不需要我们直接与房东接触。同时,而为了安全性,一般代理还会让我们进行身份验证,同时为了方便租户管理,他们还会记录相关的租户信息。这里的第三方角色就是本文将要介绍的代理模式中的代理对象。

代理模式


意图

为其他对象提供一种代理以控制对这个对象的访问

常用的结构型设计模式之一,当无法直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象需要实现相同的接口

参与者

  • Proxy
    保存一个引用使得代理可以访问实体,若RealSubject和Subject的接口相同,Proxy会引用Subject
    提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体
    控制对实体的存取,并可能负责创建和删除它

  • Subject
    定义RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。

  • RealSubject
    定义Proxy所代表的实体

代理根据其种类,在适当的时候向RealSubject转发请求

模式结构

proxy

代码实现

1.首先定义抽象的Subject,并提供Request()接口:

1
2
3
4
5
6
7
8
9
// Base Common Interface of RealSubject & Proxy
class Subject
{
public:
Subject() {};
virtual ~Subject() {};
public:
virtual void Request() = 0;
};

2.再分别定义Subject类的两个子类,真实的被代理的对象类RealSubject和代理类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
// RealSubject
class RealSubject : public Subject
{
public:
RealSubject() {};
virtual ~RealSubject() {};
public:
void Request()
{
cout << "Real Subject Request !" << endl;
}
};

// Proxy
class Proxy : public Subject
{
private:
RealSubject *m_pRealSubject;
public:
Proxy(Subject *pSubject) : m_pRealSubject(pSubject) {};
virtual ~Proxy() {};
public:
void Request()
{
if (NULL != m_pRealSubject)
{
cout << "Proxy Request !" << endl;
m_pRealSubject->Request(); // 调用真实对象类RealSubject的方法
}

}
};

3.测试代理模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void ProxyTest_General()
{
Subject *pRS = new RealSubject();
Subject *pPS = new Proxy(pRS);
pPS->Request();

if (NULL != pRS)
{
delete pRS; pRS = NULL;
}
if (NULL != pPS)
{
delete pPS; pPS = NULL;
}
}

4.运行结果:

Proxy Request !
Real Subject Request !

代理模式的分类

代理模式根据其使用场景可以分为以下几类:

远程代理

可以隐藏一个对象存在于不同地址空间的事实,负责对请求及其参数进行编码,并向不同地址空间中的实体发送已编码的请求
它使得客户端程序可以访问在远程主机上的对象,远程主机可能具有更好的计算性能与处理速度,可以快速响应并处理客户端的请求。远程代理可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户端完全可以认为被代理的远程业务对象是在本地而不是在远程,而远程代理对象承担了大部分的网络通信工作,并负责对远程业务方法的调用

为位于两个不同地址空间对象的访问提供了一种实现机制,可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率

虚拟代理

可以缓存实体的附加信息,以便延迟对它的访问
对于一些占用系统资源较多或者加载时间较长的对象,可以给这些对象提供一个虚拟代理。在真实对象创建成功之前虚拟代理扮演真实对象的替身,而当真实对象创建之后,虚拟代理将用户的请求转发给真实对象

通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度上节省系统的运行开销

缓冲代理

为某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,优化系统性能,缩短执行时间

保护代理

检查调用者是否具有实现一个请求所必需的访问权限

可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限

引用计数代理

允许在访问一个对象时有一些附加的内务处理

可以对一个对象的访问(引用)提供一些额外的操作

使用场景

  • 当客户端对象需要访问远程主机中的对象时可以使用远程代理
  • 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时
  • 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可
  • 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理
  • 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理

优缺点

  • 优点
    • 协调调用者和被调用者,在一定程度上降低了系统的耦合度
    • 不同类型的代理模式也具有其特有的优点

与适配器模式&装饰器模式的区别

  • 适配器Adapter为它所适配的对象提供了一个不同的接口。相反,代理提供了与它的实体相同的接口。然而,用于访问保护的代理可能会拒绝执行实体会执行的操作,因此,它的接口实际上可能只是实体接口的一个子集
  • 尽管Decorator的实现部分与代理相似,但Decorator的目的不一样。Decorator为对象添加一个或多个功能,而代理则控制对对象的访问

代理模式具体实例


租房问题

使用代理模式简单实现前言所述的过程

代码实现

1.定义抽象的AbstractRenter类,并提供rent接口:

1
2
3
4
5
6
// Abstract rent class 
class AbstractRenter
{
public:
virtual void rent(void) = 0;
};

2.分别实现AbstractRenter的子类RealRenterProxyRenter:

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
// real renter == house owner
class RealRenter : public AbstractRenter
{
public:
void rent(void)
{
cout << "i have a house to rent, please contact me if you need!" << endl;
}
};

// proxy renter == house proxy
class ProxyRenter : public AbstractRenter
{
private:
RealRenter _renter;
public:
ProxyRenter(RealRenter &renter) : _renter(renter) {};
public:
void rent(void)
{
validId();
_renter.rent();
logHouseUser();
}
private:
void validId(void)
{
cout << "please valid your identification" << endl;
}
void logHouseUser(void)
{
cout << "log the house user information" << endl;
}
};

3.测试代理模式:

1
2
3
4
5
6
7
void Proxy_Test_Renter(void)
{
RealRenter realRenter;
ProxyRenter proxyRenter(realRenter);
AbstractRenter &pR = proxyRenter;
pR.rent();
}

4.运行结果:

please valid your identification
i have a house to rent, please contact me if you need!
log the house user information

智能指针

使用智能引用计数代理模式实现智能指针功能

作用: 为了解决“被异常抛出时发生资源泄漏”的问题
原理:资源在局部对象构造时分配,在局部对象析构时释放。这样即使在函数执行过程时发生异常退出,也会因为异常能保证局部对象被析构从而保证资源被释放

auto_ptr析构的时候肯定会删除他所拥有的那个对象,所有我们就要注意了,一个萝卜一个坑,两个auto_ptr不能同时拥有同一个对象,在析构时都试图删除p两次删除同一个对象的行为在C++标准中是未定义的
不能用auto_ptr来管理一个数组指针,因为auto_ptr的析构函数中删除指针用的是delete,而不是delete []
构造函数的explicit关键词有效阻止从一个“裸”指针隐式转换成auto_ptr类型
auto_ptr只是一种简单的智能指针,如有特殊需求,需要使用其他智能指针,比如share_ptr
auto_ptr不能作为容器对象,STL容器中的元素经常要支持拷贝,赋值等操作,在这过程中auto_ptr会传递所有权,那么source与sink元素之间就不等价了

代码实现

1.C++ STL智能指针(auto_ptr)的简单实现Smart_Ptr:

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
 //Smart_Ptr类就是一个代理,客户只需操作Smart_Ptr的对象,而不需要与被代理的指针m_ptr打交道

template <class T>
class Smart_Ptr
{
private:
T* m_ptr;
public:
// Construction
explicit Smart_Ptr(T* ptr = 0) : m_ptr(ptr) {}; // forbide implict convert
public:
~Smart_Ptr()
{
reset();
}
public:
// Copy Construction
// not const input para , for its m_ptr changed
// release rhs's proxy control

Smart_Ptr(Smart_Ptr &rhs) : m_ptr(rhs.release()) {};

//Assignment
Smart_Ptr& operator = (Smart_Ptr &rhs)
{
// first release rhs's proxy control; secode reset to this's proxy control; thirdly return this

reset(rhs.release());
return *this;
}
public:
T* operator ->() const // 类成员函数为const ,防止更改类成员
{
return m_ptr;
}
T& operator *() const
{
return *m_ptr;
}
T* get() const
{
return m_ptr;
}
// Release Ownership
T* release()
{
T* tmp(m_ptr); // tmp = m_ptr;
m_ptr = 0;
return tmp;
}
// Reset Ownership: copy ptr to m_ptr,
void reset(T* ptr = 0)
{
if (m_ptr != ptr)
{
delete m_ptr;
m_ptr = ptr;
}
}
};

2.测试Smart_Ptr代理模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void Proxy_Test_SmartPtr()
{
// UD-SmartPtr Test:
int *p = new int(10);
// Smart_Ptr<int> sp_int_1(p); // 构造int(10)返回指针的代理sp_int
Smart_Ptr<int> sp_int_2(p); // sp_int_1 失去p控制权
Smart_Ptr<int> sp_int_3 = sp_int_2; // sp_int_1,2 均失去p控制权
Smart_Ptr<int> sp_int_4(sp_int_3); // sp_int_1,2,3 均失去p控制权
// cout << "User Defined Smart_Ptr: sp_int1 value is " << *sp_int_1 << endl;
// cout << "User Defined Smart_Ptr: sp_int2 value is " << *sp_int_2 << endl;
// cout << "User Defined Smart_Ptr: sp_int3 value is " << *sp_int_3 << endl;
cout << "User Defined Smart_Ptr: sp_int4 value is " << *sp_int_4 << endl;

// STL-AutoPtr Test:
string *p2 = new string("Hello");
auto_ptr<string> ap_string_1(p2);; // STL 标准库 auto_ptr
cout << "STL Smart_Ptr: ap_string_1 value is " << *ap_string_1 << endl;
};

3.运行结果:

User Defined Smart_Ptr: sp_int4 value is 10
STL Smart_Ptr: ap_string_1 value is Hello