欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

代理模式的应用之虚代理实现大对象的延时加载

程序员文章站 2022-05-26 08:39:55
...
代理模式的分类

(1)虚代理可以根据需要来创建“大”对象,只有到必须创建对象的时候,虚代理才会去创建对象,从而大大加快程序运行速度,并节省资源。通过虚代理可以对系统进行优化。如开发一个大文档查看软件(如图片100MB),在打开文件前,先显示缩略图,而不是将所有图片都显示出来,在需要查看图片时,再用Proxy来进行大图片的打开)

(2)保护代理:可以在访问一个对象的前后,执行很多附加的操作,除了进行权限控制之外,还可以进行很多跟业务相关的处理,而不需要修改被代理的对象。也就是说,可以通过代理来给目标对象增加功能。

(3)Copy-on-write代理:也是虚代理的一个分支,在客户端操作的时候,只有对象确实改变了,才会真的拷贝(或克隆)一个目标的对象。在实现Copy-on-write时必须对目标对象进行引用计数。代理仅会增加引用。只有当用户请求一个修改目标对象的操作时,代理才会真正的拷贝它。在这种情况下,代理还必须减少目标对象的引用次数。当引用计数为0时,将目标对象删除。

(4)远程代理:隐藏了一个对象存在于不同地址空间的事实,也即是客户通过远程代理去访问一个对象,根本就不关心这个对象在哪里,也不关心如何通过网络去访问到这个对象,从客户的角度来看,它只是在使用代理对象而己。

(5)智能指针:和保护代理类似,也是允许在访问一个对象的前后,执行很多附加的操作,这样一来就可以做很多额外的事情,比如引用计数、第1次引用一个持久对象时,将它载入内存、在访问一个实际对象前,检查是否己经锁定了它,以确保其他对象不能改变它等。

(6)Cache代理:为那些昂贵操作的结果提供临时的存储空间,以便多个客户端可以共享这些结构。

(7)防火墙代理:保护对象不被恶意用户访问或操作。

(8)同步代理:使多个用户能够同时访问目标对象而没有冲突。

代理的优点

(1)职责清晰:RealSubject就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成其他事务,附带的结果就是编程简洁清晰。

(2)可扩展性好:RealSubject可以随时变化,只要它实现了接口,甭管如何变化,代理可以在完全不做任何修改的情况下使用。

代理的使用场景

(1)需要为一个对象在不同的地址空间提供局部代表的时候,可以使用远程代理

(2)需要按照需要创建开销很大的对象的时候,可以使用虚代理

(3)需要控制对原始对象的访问的时候,可以使用保护代理

(4)需要在访问对象执行一些附加操作时,可以使用智能指引代理。

【编程实验】利用虚代理实现大对象的延时加载

代理模式的应用之虚代理实现大对象的延时加载

//声明文件

//*********************************************************************************************
/*
//结构型模式:代理模式
//场景:一次性访问多条数据问题。
//需求:一个HR(人力资源)应用项目中,当用户选择一个部门时要把这个部门下所有员工都显示出来(只显示部门ID和姓名)
        在需要的时候,可以选择查看某位员工的详细信息
//思路:由于访问多条用户数据时,基本上只需要看到用户的姓名可以考虑刚开始从数据库查询返回的用户数据只有编号和用户姓名,当客户想要详细查看某个用户数据时,再根据编号从数
        据库中获取完整的用户数据。这样可以大大减少对内存的开销
//解决方案:使用代理(虚代理),刚开始只用一个不完整的用户对象(代理对象)。当需要访问时,再由代理对象从数据库中重新获取相应的数据,而这个过程对用户来说是透明的。
*/

#include <iostream>
#include <string>
#include <vector>

using namespace std;


//////////////////////////////////////////////////////////////////////////
// 字符串分割
//
// -------------------------------------------------------------------------
// 函数     : Split
// 功能     : 分割STL标准字符串
// 返回值   : void
// 参数     : Container<std::basic_string<CharT> >& v 存放分割结果
// 参数     : const std::basic_string<CharT>& s 待分割字符串
// 参数     : const std::basic_string<CharT>& c 分割字符串
// -------------------------------------------------------------------------
template<typename CharT, template<typename S, typename Q = std::allocator<S> > class Container>
void Split(Container<std::basic_string<CharT> >& v, const std::basic_string<CharT>& s, const std::basic_string<CharT>& c);

//******************************辅助类***************************
//测试数据(在内存中模拟数据库中的值)
class CEmpDb{
private:
	CEmpDb(){}
public:
	static vector<string> vUser;//人员列表
	static vector<string> vDept;//部门列表
};
//人员列表初始数据


//部门列表初始数据
//编号规则:如010201:表示总公司下的二分公司里的开发部

//**************************************代理类****************************
//定义用户数据对象的接口
class CAbsEmp{
public:
	virtual void SetNo(string no) = 0;
	virtual string GetNo() = 0;
	virtual void SetName(string name) = 0;
	virtual string GetName() = 0;
	virtual void SetDept(string dept) = 0;
	virtual string GetDept() = 0;
	virtual void SetSex(string sex) = 0;
	virtual string GetSex() = 0;
};

//真实的目标对象——描述用户数据的对象
class CRealEmp : public CAbsEmp{
private:
	string strNo; //用户编号
	string strName; //用户姓名
	string strDept;//部门编号
	string strSex;//用户性别
public:
	void SetNo(string no);
	string GetNo();
	void SetName(string name);
	string GetName();
	void SetDept(string dept);
	string GetDept();
	void SetSex(string sex);
	string GetSex();
};
//代理对象,代理用户数据对象
class CProxyEmp : public CAbsEmp{
private:
	CAbsEmp* pRealEmp;//持有被代理的具体的目标对象
	bool bLoaded;//用来标识是否己经重新装载过数据了
protected:
	void QueryFromDb();
public:
	CProxyEmp();
	~CProxyEmp();
	void SetNo(string no);
	string GetNo();
	void SetName(string name);
	string GetName();
	void SetDept(string dept);
	string GetDept();
	void SetSex(string sex);
	string GetSex();
};

//实现示例要求的功能
class CCompanyMgr{
private:
	static vector<CAbsEmp*> vEmp;
public: //根据部门编号来获取该部门下的所有人员
	static void GetUserByDept(string dept);
	static void RemoveAll();
};

//实现文件

template<template<typename S, typename Q = std::allocator<S> > class Container>
void Split(Container<std::basic_string<char> >& v, const std::basic_string<char>& s, const std::basic_string<char>& c)
{
	if (0 == c.length())
		return;

	std::basic_string<char>::size_type pos1 = 0;
	std::basic_string<char>::size_type pos2 = 0;

	pos1 = 0;
	pos2 = s.find(c);
	while (std::basic_string<char>::npos != pos2)
	{
		v.push_back(s.substr(pos1, pos2 - pos1));

		pos1 = pos2 + c.size();
		pos2 = s.find(c, pos1);
	}

	if (pos1 != s.length())
	{
		v.push_back(s.substr(pos1));
	}
}
//******************************辅助类***************************
//测试数据(在内存中模拟数据库中的值)

//人员列表初始数据
static vector<string>::value_type DbEmpData[] = 
{
	vector<string>::value_type("No001,ZhangSan1,010101,male"),
	vector<string>::value_type("No002,ZhangSan2,010101,male"),
	vector<string>::value_type("No003,ZhangSan3,010102,male"),
	vector<string>::value_type("No004,ZhangSan4,010201,male"),
	vector<string>::value_type("No005,ZhangSan5,010201,male"),
	vector<string>::value_type("No006,ZhangSan6,010202,male")
};
vector<string> CEmpDb::vUser(DbEmpData, DbEmpData+6);

//部门列表初始数据
//编号规则:如010201:表示总公司下的二分公司里的开发部
static vector<string>::value_type DbDeptData[] = 
{
	vector<string>::value_type("01,Company"),//总公司
	vector<string>::value_type("0101,BranchCompany1"),//一分公司
	vector<string>::value_type("0102,BranchCompany1"),//二分公司
	vector<string>::value_type("010101,DevelopDep"),//开发一部
	vector<string>::value_type("010102,TestDep"),//测试部
	vector<string>::value_type("010201,DevelopDep"),//开发二部
	vector<string>::value_type("010202,ServiceDep")//客服部
};
vector<string> CEmpDb::vDept(DbDeptData, DbDeptData+7);
//**************************************代理类****************************
//定义用户数据对象的接口

//真实的目标对象——描述用户数据的对象
void CRealEmp::SetNo(string no){strNo = no;}
string CRealEmp::GetNo(){return strNo;}
void CRealEmp::SetName(string name){strName = name;}
string CRealEmp::GetName(){return strName;}
void CRealEmp::SetDept(string dept){strDept = dept;}
string CRealEmp::GetDept(){return strDept;}
void CRealEmp::SetSex(string sex){strSex = sex;}
string CRealEmp::GetSex(){return strSex;}
//代理对象,代理用户数据对象

void CProxyEmp::QueryFromDb()//重新询数据库以获取完整的用户数据
{
	cout << "Reload Full Info From Db, EmpNo = " << pRealEmp->GetNo() << endl;
	vector<string> vString;
	for(vector<string>::iterator it = CEmpDb::vUser.begin(); it != CEmpDb::vUser.end(); it++){
		vString.clear(); Split(vString, *it, ",");
		if(vString.size() != 4 || vString[0] != pRealEmp->GetNo())			continue;
		pRealEmp->SetDept(vString[2]); pRealEmp->SetSex(vString[3]);
	}
}
CProxyEmp::CProxyEmp() : bLoaded(false){pRealEmp = new CRealEmp();}
CProxyEmp::~CProxyEmp(){if(pRealEmp != NULL)			delete pRealEmp;}
void CProxyEmp::SetNo(string no){ pRealEmp->SetNo(no);}
string CProxyEmp::GetNo(){ return pRealEmp->GetNo(); }
void CProxyEmp::SetName(string name){ pRealEmp->SetName(name);}
string CProxyEmp::GetName(){ return pRealEmp->GetName();}
void CProxyEmp::SetDept(string dept){ pRealEmp->SetDept(dept);}
string CProxyEmp::GetDept(){ 
	if(!bLoaded){
		QueryFromDb(); bLoaded = true;
	}
	return pRealEmp->GetDept();
}
void CProxyEmp::SetSex(string sex){ pRealEmp->SetSex(sex);}
string CProxyEmp::GetSex(){ 
	if(!bLoaded){//从数据库中重新装载
		QueryFromDb(); bLoaded = true;//设置重新装载标志为true
	}
	return pRealEmp->GetSex();
}

//实现示例要求的功能
//根据部门编号来获取该部门下的所有人员
void CCompanyMgr::GetUserByDept(string dept)
{
	RemoveAll();
	//只查询userId和name两个值就可以了 实际中,这里须访问数据库
	vector<string> vString;
	for(vector<string>::iterator it = CEmpDb::vUser.begin(); it != CEmpDb::vUser.end(); it++){
		vString.clear(); Split(vString, *it, ",");
		if(vString.size() != 4 || vString[2].substr(0, 4) == dept)			continue;
		CAbsEmp* pProxyEmp = new CProxyEmp(); 
		pProxyEmp->SetNo(vString[0]); pProxyEmp->SetName(vString[1]);
		vEmp.push_back(pProxyEmp);  //只设置userId和name两个值就可以了
	}
	cout << "如果只是显示用户名称,则不需要重新查询数据库" << endl;
	cout << "*********************************************************" << endl;
	for(vector<CAbsEmp*>::iterator it = vEmp.begin(); it != vEmp.end(); it++){
		cout << "No : " << (*it)->GetNo() << ", Name : " << (*it)->GetName() << endl;	
	}
	cout << "*********************************************************" << endl << endl;

	cout << "如果访问非用户编号和用户姓名外的属性,那就会重新查询数据库" << endl;
	cout << "*********************************************************" << endl;
	for(vector<CAbsEmp*>::iterator it = vEmp.begin(); it != vEmp.end(); it++){
		cout << "No : " << (*it)->GetNo() << ", Name : " << (*it)->GetName() << ", Dept : " << (*it)->GetDept() << ", Sex : " << (*it)->GetSex() << endl;
	}
	cout << "*********************************************************" << endl << endl;
}
void CCompanyMgr::RemoveAll()
{
	for(vector<CAbsEmp*>::iterator it = vEmp.begin(); it != vEmp.end(); ){
		CAbsEmp* pEmp = (*it); 
		if(pEmp != NULL)			delete pEmp;
		it = vEmp.erase(it);
	}
}
vector<CAbsEmp*> CCompanyMgr::vEmp;

//测试客户端

void main()
{
	CCompanyMgr::GetUserByDept("010101");
	CCompanyMgr::RemoveAll();
}