Proxy also a.k.a. Surrogate
对于复杂的软件系统常常有一种处理手法,即增加一层间接层,从而使得系统获得一种更为灵活、满足特定需求的解决方案。在面向对象的系统中,有些对象由于某种原因,比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等,直接访问会给使用者或者系统结构带来很多麻烦。
Proxy设计模式就是在不失去透明操作对象的同时,通过增加一层间接层来管理、控制这些对象特有的复杂性。
图1
图2
表示不同的进程
图1中假定A和B位于不同的进程,典型的情况是A和B位于互联网上两台不同的机器上,如果A要访问B三次,那么A就要如上图调用B三次。
图2中增加一个B的代理,该代理和A位于同一个进程内,当A要调用B的功能时,A仅需要调用B的代理即可,然后B的代理和B之间进行通信。另外如果A要3次调用B的功能,那么A仅需要3此调用B的代理即可,而B的代理和B之间有可能只需要一次通信就够了,由此可见,B的代理有一定的缓冲功能。
Provide a surrogate or placeholder for another object to control access to it. (为其他对象提供一种代理以控制对这个对象的访问) -GoF
Proxy设计模式之静态结构UML类图:
图3
说明:
Service:
The interface that both the proxy and the real object will implement. (代理和实际对象必须实现的接口)
ServiceProxy:
ServiceProxy implements Service and forwards method calls to the real object (ServiceImplemention) when appropriate.
(ServiceProxy实现了Service接口,并在合适的时候,将请求传递给实际对象ServiceImplementation)
ServiceImplementation:
The real, full implementation of the interface. This object will be represented by the Proxy object.
(真正、完整的接口实现,该对象由Proxy对象代表)
A proxy (or stub) is a representative for another object. To enable the proxy to represent the real object, the proxy has to implement the exact same interface as the real object. Furthermore, the proxy keeps a reference to the real object. The proxy needs the reference to the real object so that it can call methods on the real object if necessary. The clients will be interacting with the proxy, but the proxy can delegate the execution to the real object. The proxy implements the same interface as the real object, but can perform tasks that the real object does not, such as remote communication or security.
Use the Proxy pattern when you need a more elaborate reference to an object instead of just a regular one:
- Remote proxy: When you need a local representative for an object in another address space(e.g. JVM)
- Virtual proxy: Acts as a placeholder and delays creating expensive objects.
- Protection proxy: Determines access rights to the real object.
Remote Proxy C++模拟代码:
// Proxy.h
#include <string>
#include <iostream>
#include <string>
using namespace std;
class IEmployee
{
public:
virtual string get_name(int ID) = 0;
virtual int get_age(int ID) = 0;
virtual double get_salary(int ID) = 0;
public:
virtual ~IEmployee();
};
IEmployee::~IEmployee()
{
cout << "in the destructor of IEmployee..." << endl;
}
class Employee : public IEmployee
{
public:
string get_name(int ID);
int get_age(int ID);
double get_salary(int ID);
~Employee();
};
string Employee::get_name(int ID)
{
// ... 假定此处查询数据库,获得ID对应员工的姓名
string name = "玄机逸士";
return name;
}
int Employee::get_age(int ID)
{
// ... 假定此处查询数据库,获得ID对应员工的年龄
int age = 27;
return age;
}
double Employee::get_salary(int ID)
{
// ... 假定此处查询数据库,获得ID对应员工的工资
double salary = 50000.1;
return salary;
}
Employee::~Employee()
{
cout << "in the destructor of Employee..." << endl;
}
class EmployeeProxy : public IEmployee
{
public:
string get_name(int ID);
int get_age(int ID);
double get_salary(int ID);
~EmployeeProxy();
};
string EmployeeProxy::get_name(int ID)
{
// ...假定此处通过socket或者RPC等其他方式访问Employee中的get_name(int ID)方法,并接受相应的返回值
string name = "玄机逸士";
return name;
}
int EmployeeProxy::get_age(int ID)
{
// ...假定此处通过socket或者RPC等其他方式访问Employee中的get_age(int ID)方法,并接受相应的返回值
int age = 27;
return age;
}
double EmployeeProxy::get_salary(int ID)
{
// ...假定此处通过socket或者RPC等其他方式访问Employee中的get_salary(int ID)方法,并接受相应的返回值
double salary = 50000.1;
return salary;
}
EmployeeProxy::~EmployeeProxy()
{
cout << "in the destructor of EmployeeProxy..." << endl;
}
// Proxy.cpp
#include "Proxy.h"
int main(int argc, char **argv)
{
IEmployee *employee = new EmployeeProxy;
cout << employee->get_name(10) << endl;
cout << employee->get_age(10) << endl;
cout << employee->get_salary(10) << endl;
delete employee;
return 0;
}
输出结果:
玄机逸士
27
50000.1
in the destructor of EmployeeProxy…
in the destructor of IEmployee…
说明:
- IEmployee是接口,相当于图3中的Service;
- Employee是在远程地址空间中对接口IEmployee的实现;
- EmployeeProxy是在本地地址空间中对IEmployee的实现,同时它通过网络依赖Employee
- main函数(HRSystem)是客户程序。它向EmployeeProxy发出请求,EmployeeProxy得到HRSystem发出的请求后,通过网络将请求提交给远程的Employee,Employee接受请求并做出相关处理,将处理的结果返回给EmployeeProxy,EmployeeProxy将结果返回给HRSystem。
图4
* 要完整实现一个Remote Proxy需要比较多的代码,在此就用程序中的部分注释来代替了。
Virtual Proxy C++示例代码:
// Proxy.h
#define FILENAME "contacts.cbf"
// 一个简单的联系人类
class Contact
{
private:
string name;
string email;
public:
Contact()
{
}
Contact(string name, string email) : name(name), email(email)
{
}
~Contact()
{
cout << "in the destructor of Contact..." << endl;
}
public:
// 下面几个成员函数都是getter
string get_name() const
{
return name;
}
string get_email() const
{
return email;
}
};
// 联络本:一个抽象类,用作接口,它规定了联络本的相关方法
class IContactBook
{
protected:
public:
virtual void add(const Contact& contact) = 0; // 增加一个联系人
virtual vector<Contact> get_contact(const string& name) = 0; // 查询联系人,有可能重名
virtual multimap<string, Contact> get_all_contacts() = 0; // 获取所有联系人
virtual void save() = 0; // 保存数据
public:
virtual ~IContactBook()
{
cout << "in the destructor of IContactBook..." << endl;
}
};
// ContactBook继承IContactBook,该类实现了IContactBook中所有规定的方法,
// 它是一个“real object”(真实对象),将由ContactBookProxy来代理
class ContactBook : public IContactBook
{
private:
multimap<string, Contact> contacts;
public:
ContactBook()
{
Contact *contact = new Contact;
ifstream file(FILENAME, ios::in|ios::binary); // 打开文件
if(!file)
{
cout << "can not open file..." << endl;
return;
}
Else // 将文件中的数据读入contacts
{
file.seekg (0, ios::end);
int length = file.tellg();
file.seekg (0, ios::beg);
int element_number = length / sizeof(Contact);
cout << "element_number = " << element_number << endl;
for(int i = 0; i < element_number; i++)
{
file.seekg(sizeof(Contact) * i, ios::beg);
file.read((char *)contact, sizeof(Contact));
contacts.insert(pair<string, Contact>(contact->get_name(), *contact));
}
}
file.close();
delete contact;
contact = 0;
}
~ContactBook()
{
//save();
cout << "in the destructor of ContactBook..." << endl;
}
public:
// 根据名字获取联系人,由于有可能重名,因此返回值是一个vector<Contact>
vector<Contact> get_contact(const string& name)
{
multimap<string, Contact>::iterator it;
pair<multimap<string, Contact>::iterator, multimap<string, Contact>::iterator> ret;
ret = contacts.equal_range(name);
vector<Contact> contact_found;
for(it = ret.first; it != ret.second; it++)
{
contact_found.push_back(it->second);
}
return contact_found;
}
// 增加一个联系人
void add(const Contact& contact)
{
string email;
vector<Contact> vcontact = get_contact(contact.get_name());
// 如果multimap contacts中不存在该联系人
if(vcontact.empty())
{
// 则将联系人contact加入到multimap中,联系人contact的name作为key
contacts.insert(pair<string, Contact>(contact.get_name(), contact));
}
else // 同名的联系人
{
email = contact.get_email();
int i = 0;
for(vector<Contact>::iterator it = vcontact.begin(); it != vcontact.end(); it++)
{
if(!(email == it->get_email())) // 同名的联系人,但email不同。在这里,
{ // 我们假定email是唯一的,实际情况大多如此
i++;
}
}
if(i == vcontact.size()) // 和找出来所有同名的人的email地址都不一样
{
contacts.insert(pair<string, Contact>(contact.get_name(), contact));
return;
}
}
}
// 将contacts中的所有联系人存入到文件中
void save()
{
ofstream file(FILENAME, ios::out|ios::binary);
if(!file)
{
cout << "can not open file..." << endl;
return;
}
for(multimap<string, Contact>::iterator it = contacts.begin(); it != contacts.end(); it++)
{
file.write((const char *)(&(it->second)), sizeof(Contact));
}
file.close();
}
// 获取所有联系人
multimap<string, Contact> get_all_contacts()
{
return contacts;
}
// 一个辅助函数,将被Proxy调用
void set_contacts(const multimap<string, Contact>& lcontacts)
{
contacts.erase(contacts.begin(), contacts.end());
contacts = lcontacts;
}
};
// 代理
class ContactBookProxy : public IContactBook
{
private:
IContactBook *contact_book; // 存放真实对象
multimap<string, Contact> local_contacts; // 本地联系人数据
public:
ContactBookProxy()
{
contact_book = new ContactBook; // 创建真实对象
local_contacts = contact_book->get_all_contacts(); // 将真实对象中的数据取到本地
delete contact_book; // 取出数据后,立即销毁真实对象
contact_book = 0; // 此后,均指操作本地联系人数据local_contacts
}
~ContactBookProxy()
{
cout << "in the destructor of ContactBookProxy..." << endl;
if(!contact_book)
{
delete contact_book;
contact_book = 0;
}
}
vector<Contact> get_contact(const string& name)
{
// 只操作本地数据local_contacts,因为此时local_contacts已经包含了真实对象中的所有数据
multimap<string, Contact>::iterator it;
pair<multimap<string, Contact>::iterator, multimap<string, Contact>::iterator> ret;
ret = local_contacts.equal_range(name);
vector<Contact> contact_found;
for(it = ret.first; it != ret.second; it++)
{
contact_found.push_back(it->second);
}
return contact_found;
}
void add(const Contact& contact)
{
string email;
vector<Contact> vcontact = get_contact(contact.get_name());
// 如果multimap contacts中不存在该联系人
if(vcontact.empty())
{
// 则将联系人contact加入到multimap中,联系人contact的name作为key
local_contacts.insert(pair<string, Contact>(contact.get_name(), contact));
}
else // 同名的联系人
{
email = contact.get_email();
int i = 0;
for(vector<Contact>::iterator it = vcontact.begin(); it != vcontact.end(); it++)
{
if(!(email == it->get_email())) // 同名的联系人,但email不同。在这里,
{ // 我们假定email是唯一的,实际情况大多如此
i++;
}
}
if(i == vcontact.size()) // 和找出来所有同名的人的email地址都不一样
{
local_contacts.insert(pair<string, Contact>(contact.get_name(), contact));
return;
}
}
}
void save()
{
if(!contact_book)
{
contact_book = new ContactBook;
}
ContactBook *cb = dynamic_cast<ContactBook*>(contact_book); // down casting
cb->set_contacts(local_contacts);
contact_book->save();
}
multimap<string, Contact> get_all_contacts()
{
return local_contacts;
}
};
// Proxy.cpp
int main(int argc, char **argv)
{
IContactBook *contact_proxy = new ContactBookProxy;
contact_proxy->add(Contact("玄机逸士", "[email protected]"));
contact_proxy->add(Contact("玄机逸士", "[email protected]"));
contact_proxy->add(Contact("上官天野", "[email protected]"));
contact_proxy->add(Contact("上官天野", "[email protected]"));
contact_proxy->add(Contact("上官天野", "[email protected]"));
contact_proxy->add(Contact("黄家四娘", "[email protected]"));
contact_proxy->save();
vector<Contact> vec_contact = contact_proxy->get_contact("上官天野");
for(vector<Contact>::const_iterator it = vec_contact.begin(); it != vec_contact.end(); it++)
{
cout << it->get_name() << " : " << it->get_email() << endl;
}
cout << "------------------------------------" << endl;
multimap<string, Contact> map_all_contacts(contact_proxy->get_all_contacts());
for(multimap<string, Contact>::iterator it = map_all_contacts.begin(); it != map_all_contacts.end(); it++)
{
cout << it->first << " : " << (it->second).get_email() << endl;
}
delete contact_proxy;
return 0;
}
输出结果:
element_number = 5
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of ContactBook...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of IContactBook...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
element_number = 5
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
上官天野 : [email protected]
上官天野 : [email protected]
上官天野 : [email protected]
------------------------------------
黄家四娘 : [email protected]
上官天野 : [email protected]
上官天野 : [email protected]
上官天野 : [email protected]
玄机逸士 : [email protected]
in the destructor of ContactBookProxy...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of IContactBook...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
in the destructor of Contact...
上述代码的要点是:首先将真实对象中的数据拷贝到代理,并立即销毁真实对象,然后客户端程序一直对代理进行操作,直到所需要的操作完成后,将代理中的结果返回给真实对象,并由真实对象保存到文件中。严格地说,这个示例代码并不是真正意义上的Virtual Proxy,因为它并没有延迟真实对象的创建。不过要实现真正意义上Virtual Proxy也并不困难。
Protection proxy的示例代码,和前面的代码差不多,只是在proxy的成员函数在调用与之对应的真实对象的成员函数之前,进行保护性检查。如果通过了检查则调用真实对象的对应成员函数,否则,不调用并提示相关信息。具体代码略。