设计模式——代理模式

1. 概述

  代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。是属于结构型模式。

2. 结构图

  下面是代理模式(Proxy)的结构图:

  • Proxy:保存一个引用使得代理可以访问实体。若RealSubject和Subject的接口相同,Proxy会引用Subject;提供一个与Subject的接口相同的接口,这样代理就可以来代替实体;控制对实体的存取,并可能负责创建和删除它。
  • Subject:定义RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。
  • RealSubject:定义Proxy所代表的实体。
  代理根据其种类,在适当的时候向RealSubject转发请求。
  Proxy模式在访问对象时引入了一定程度的间接性。根据代理的类型,附加的间接性有很多种用途:
  1. 远程代理(Remote Proxy)可以隐藏一个对象存在于不同地址空间的事实。
  2. 虚代理(Virtual Proxy)可以进行最优化,例如根据要求创建对象。
  3. 保护代理(Protection Proxies)和智能引用(Smart Reference)都允许在访问一个对象时有一些附加的内务处理。
  Proxy模式还可以对用户隐藏另一种称之为copy-on-write的优化方式,该优化与根据需要创建对象有关。拷贝一个庞大而复杂的对象是一种开销很大的操作,如果这个拷贝根本没有被修改,那么这些开销就没必要。用代理延迟这一拷贝过程,我们可以保证只有当这个对象被修改的时候才对它进行拷贝。在实现copy-on-write时必须对实体进行引用计数。拷贝代理仅会增加引用计数。只有当用户请求一个修改该实体的操作时,代理才会真正的拷贝它。在这种情况下,代理还必须减少实体的引用计数。当引用计数为零,这个实体将被删除。

3. 实例分析

  对一个对象进行访问控制的一个原因是为了只有在我们确实需要这个对象时才对它进行创建和初始化。
  我们考虑一个可以在文档中嵌入图形对象的文档编辑器。有些图形对象的创建开销很大。但是打开文档必须很迅速,因此我们打开文档时应该避免一次性创建所有开销很大的对象。因为并非所有这些对象在文档中都同时可见,所以也没有必要同时创建这些对象。
  这一限制条件意味着,对于每一个开销很大的对象,应该根据需要进行创建,当一个图形变为可见时会产生这样的需要。但是在文档中我们用什么来替代这个图像呢?我们又如何才能隐藏根据需要创建图像这一事实,从而不会使得编辑器的实现复杂化呢?例如,这种优化不应该影响绘制和格式化的代码。
  问题的解决方案是使用另一个对象,即图像Proxy,替代那个真正的图像。Proxy可以代替一个图形对象,并且在需要时负责实例化这个图像对象。
  只有当文档编辑器激活图像代理的Draw操作以显示这个图像的时候,图像Proxy才创建真正的图像。Proxy直接将随后的请求转发给这个图像对象。因此在创建这个图像以后,它必须有一个指向这个图像的引用。
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  Graphic.h
*Author:  Huangjh
*Version:
*Date:  2017-11-21
*Description:  相当于结构图中的Subject角色——定义嵌入图形的访问接口
*Others:
**********************************************************************************/
#ifndef _GRAPHIC_H
#define _GRAPHIC_H

class CGraphic
{
public:
	CGraphic() { }
	virtual ~CGraphic() { }

	virtual void Draw(void) = 0;
};

#endif	//#ifndef _GRAPHIC_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  Image.h
*Author:  Huangjh
*Version:
*Date:  2017-11-21
*Description:  相当于结构图中的RealSubject角色——某个创建开销很大的图像对象
*Others:
**********************************************************************************/
#ifndef _IMAGE_H
#define _IMAGE_H

#include <iostream>
#include "Graphic.h"

class CImage : public CGraphic
{
public:
	CImage() 
	{
		std::cout << "某个图像对象,创建开销很大,速度慢" << std::endl;
	}
	~CImage() { }

	void Draw(void)
	{
		std::cout << "绘制出这个开销很大的图像对象" << std::endl;
	}
};

#endif	//#ifndef _IMAGE_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  ImageProxy.h
*Author:  Huangjh
*Version:
*Date:  2017-11-21
*Description:  相当于结构图中的Proxy角色——某个开销很大的图像代理
*Others:
**********************************************************************************/
#ifndef _IMAGE_PROXY_H
#define _IMAGE_PROXY_H

#include <new>
#include "Graphic.h"
#include "Image.h"

class CImageProxy	:	public CGraphic
{
public:
	CImageProxy()
		:m_pImage(NULL)
	{
		std::cout << "图像Proxy,替代那个真正的图像,创建很快" << std::endl;
	}
	~CImageProxy()
	{
		if (m_pImage)
			delete m_pImage;
	}

	void Draw(void)
	{
		try
		{
			if (m_pImage == NULL)
				m_pImage = new CImage();
		}
		catch (std::bad_alloc &ex)
		{
			std::cout << "Throw bad_alloc: " << ex.what() << std::endl;
			abort();
		}
		m_pImage->Draw();
	}

private:
	CImage *m_pImage;
};

#endif	//#ifndef _IMAGE_PROXY_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  main.cpp
*Author:  Huangjh
*Version:
*Date:  2017-11-21
*Description:  代理模式的测试用例
*Others:
**********************************************************************************/
#include "Graphic.h"
#include "ImageProxy.h"

int main(void)
{
	//例如这里打开文档,文档里面有个开销很大的对象,该对象并不在文档开始可见,使用代理模式避免打开满的问题
	std::cout << "打开文档——创建图像Proxy" << std::endl;
	CGraphic *pImageProxy = new CImageProxy();
	//文档打开后,我们将浏览到该图像对象
	std::cout << "浏览到该对象——需要创建和绘制" << std::endl;
	pImageProxy->Draw();

	return 0;
}
  运行结果如下所示:
打开文档——创建图像Proxy
图像Proxy,替代那个真正的图像,创建很快
浏览到该对象——需要创建和绘制
某个图像对象,创建开销很大,速度慢
绘制出这个开销很大的图像对象

4. 优缺点

4.1 优点

  • 代理模式能够协调调用者和被调用者吗,在一定程度上降低了系统的耦合度。
  • 远程代理是的客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
  • 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。
  • 保护代理可以控制对真实对象的使用权限。

4.2 缺点

  • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
  • 实现代理模式需要额外的工作,有些代理秘书的实现比较复杂。

5. 适用性

  在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。下面是一些可以使用Proxy模式常见的情况:
  1. 远程代理(Remote Proxy) 为一个对象在不同的地址空间提供局部代表。
  2. 虚代理(Virual Proxy) 根据需要创建开销很大的对象。
  3. 保护代理(Protection Proxy) 控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
  4. 智能指引(Smart Reference) 取代了简单的指针,它在访问对象时执行一些附加操作。
  它(智能指引)的典型用途包括:
  • 对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它。
  • 当第一次引用一个持久对象时,将它装入内存。
  • 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

6. 参考资料

  • 《大话设计模式》
  • 《设计模式——可复用的面向对象软件的基础》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值