【Python设计模式】05 代理模式-控制对象的访问

五、代理模式-控制对象的访问 Python3.x

本章进一步学习结构型设计模式中的代理模式

本章主题

  • 介绍代理和代理设计模式
  • 代理模式的UML 图
  • 代理模式的变体
  • 利用Python3.x 代码实现的真实用例
  • 代理模式的优点
  • 门面模式和代理模式之间的比较
  • 常见问题

1. 理解代理设计模式

代理通常就是一个介于寻求方和提供方之间的中介系统
在Web 世界中,它相当于代理服务器
代理服务器可以封装请求、保护隐私,并且非常适合在分布式结构中运行

在设计模式的上下文中,代理是充当实际对象接口的类

代理模式应用场景:

  • 它能够以更简单的方式表示一个复杂的系统。例如,设计多个复杂计算或过程的系统应该提供一个更简单的接口,让它充当客户端的代理
  • 它提高了现有的实际对象的安全性。在许多情况下,都不允许客户端直接访问实际对象。这是因为实际对象可能受到恶意活动的危害。这时候,代理就能起到抵御恶意活动的盾牌作用,从而保护了实际对象
  • 它为不同服务器上的远程对象提供本地接口。例如,客户端希望在远程系统上运行某些命令的分布式系统,但客户端可能没有直接的权限来实现这一点。因此它将请求转交给本地对象(代理),然后由远程机器上的代理执行该请求
  • 它为消耗大量内存的对象提供了一个轻量级句柄。有时,你可能不想加载主要对象,除非他们真的有必要。这是因为实际对象真的很笨重,可能需要消耗大量资源,如:网站用户的个人简介头像。你最好在列表视图中显示简介头像的缩略图,当然,为了展示用户简介的详细介绍,你就需要加载实际图片了

下面的Python 代码实现了代理模式
演员,经纪人(代理),制作公司

# 演员类
class Actor(object):
	def __init__(self):
		self.isBusy = False
		self.name = "周星驰"
	
	def occpied(self):
		self.isBusy = True
		print(self.name, "没空,正在拍摄电影")

	def available(self):
		self.isBusy = False
		print(self.name, "有空,可以谈合约")

	def getStatus(self):
		return self.isBusy

# 代理类
class Agent(object):
	def __init__(self):
		self.principal = None
	
	def work(self):
		self.actor = Actor()
		if self.actor.getStatus():
			self.actor.occupied()
		else:
			self.actor.available()


if __name__ == '__main__':
	r = Agent()
	r.work()

运行结果:

周星驰 有空,可以谈合约


代理设计模式主要完成了以下工作:

  • 为其它对象提供了一个代理,从而实现了对原始对象的访问控制
  • 可以用作一个层或接口,以支持分布式访问
  • ta通过增加代理,保护真正的组件不受意外的影响

2. 代理模式的UML类图

【Python设计模式】05 代理模式-控制对象的访问_第1张图片

我们发现这个模式有3个主要的参与者:

  • 代理:它维护一个引用,允许代理(Proxy)通过这个引用来访问实际对象。它提供了一个与主题(Subject)相同的接口,以便代理可以替换真实的主题。代理还负责创建和删除真实主题(RealSubject)
  • 主题:它定义了RealSubject 和Proxy 的公共接口。以Proxy 和RealSubject 的形式实现主题(Subject),使用RealSubject 的任何地方都可以使用代理(Proxy)
  • 真实主题:它定义代理(Proxy)所代表的真实对象

从数据结构的角度看,UML 图可以表示如下:
1.代理:它是一个控制对RealSubject 类访问的类。它处理客户端的请求,负责创建或删除RealSubject。

2.主题/真实主题:主题是定义真实主题(RealSubject)和代理(Proxy)相类似的接口。RealSubject是Subject接口的实际实现。它提供了真正的功能,然后由客户端使用。

3.客户端:它访问要完成工作的 Proxy类。Proxy类在内部控制对 RealSubject的访问,并引导客户端所请求的工作。

3. 了解不同类型的代理

1.虚拟代理:

如果一个对象实例化后会占用大量内存的话,可以先利用占位符来表示,这就是所谓的虚拟代理。如:网站加载大型图片, 而这个图片需要很长时间才能加载完成。通常开发人员将在网页上创建一个占位符图标,以提示这里有图像。但是,只有当客户实际点击图标时才会加载图像,从而节省开销。因此,虚拟代理中,当客户端请求或访问对象时,才会创建实际对象。

2.远程代理:

它给远程服务器或不同地址空间上的实际对象提供了一个本地表示。如:应用程序建立一个监控系统,应该设计多个web服务器,数据服务器,任务服务器,缓存服务器。如果我们要监视这些服务器的 CPU 和磁盘利用率,就需要建立一个对象,该对象能够 用于监视应用程序运行的上下文中,同时还可以执行远程命令以获取实际的参数值。在这种情况下,建立一个作为远程对象的本地表示的远程代理对象将可以帮助我们实现这个目标。

3.保护代理:

这种代理能够控制 RealSubject的敏感对象的访问。如:分布式系统中,web提供多个服务,服务有相互合作提供各种功能。认证服务充当负责认证和授权的保护性代理服务器。代理自然有助于保护网站的核心功能,防止无法识别或未授权的代理 访问他们。因此,代理对象会检查调用者是否具有转发请求所需的访问权限。

4.智能代理:

智能代理在访问对象时插入其他操作。如:假设系统中有一个核心组件,它将状态信息集中保存在一个地点。通常情况下, 这样的组件会被多个不同的服务器调 用以完成他们的任务,并且可能导致资源共享问题。与让服务器直接调用核心组件不同,智能代理是内置的,并且会在访问之前检查实际对象是否被锁定,以确保没有其他对象可以更改它。

4. 现实世界中的代理模式

# coding:utf-8
from abc import ABCMeta, abstractmethod

# 主题
class Payment(metaclass=ABCMeta):
	@abstractmethod
	def do_pay(self):
		pass
	
# 真实主题
class Bank(Payment):
	def __init__(self):
		self.card = None
		self.account = None
	
	def __getAccount(self):
		self.account = self.card  #假定卡号就是账户
		return self.account

	def __hasFunds(self):
		print("银行:核对账户", self.__getAccount(), "余额足够")
		return True

	def setCard(self, card):
		self.card = card
	
	def do_pay(self):
		if self.__hasFunds():
			print("银行:: 商家付款")
			return True
		else:
			print("银行:: 对不起,余额不足")
			return False

# 代理
class DebitCard(Payment):
	def __init__(self):
		self.bank = Bank()
	
	def do_pay(self):
		card = input("代理:: 输入卡号")
		self.bank.setCard(card)
		return self.bank.do_pay()

# 客户端
class You:
	def __init__(self):
		print("客户端:: 让我买一件T恤衫")
		self.debitCard = DebitCard()
		self.isPurchased = None
	
	def make_payment(self):
		self.isPurchased = self.debitCard.do_pay()

	def __del__(self):
		if self.isPurchased:
			print("客户端:: 哇!这件衣服是我的了 :-)")
		else:
			print("客户端:: 我的余额不足,sorry :-(")


you = You()
you.make_payment()

运行结果:


客户端:: 让我买一件T恤衫
代理:: 输入卡号666
银行:核对账户 666 余额足够
银行:: 商家付款
客户端:: 哇!这件衣服是我的了 ?


5. 代理模式的优点

  • 代理可以通过缓存笨重的对象或频繁访问的对象来提高应用程序的性能
  • 代理还提供对于真实主题的访问授权。因此,只有提供合适的权限的情况下,这个模式才会接受委派
  • 远程代理还便于与可用作网络连接和数据库连接的远程服务器进行交互,并且可以用于监控系统

6. 门面模式与代理模式之间的比较

代理模式 门面模式
ta为其他对象提供了代理或占位符,以控制对原始对象的访问 ta为类的大型子系统提供了一个接口
代理对象具有与目标对象相同的接口,并保存有目标对象的引用 ta实现了子系统之间的通信和依赖性的最小化
ta充当客户端和被封装的对象之间的中介 门面对象提供了单一的简单接口

7. 常见问答

1. 装饰器模式和代理模式之间有什么区别?
答:装饰器向在运行时装饰的对象添加行为,而代理则是控制对对象的访问。代理和真实主题之间的关联是在编译时完成的,而不是动态的

2. 代理模式的缺点是什么?
答:代理模式会增加响应时间。例如,如果代理没有良好的体系结构或存在性能问题,它就会增加真实主题的响应时间。一般来说,这一切都取决于代理写得有多好

3. 客户端可以独立访问真实的主题吗?
答:是的,但是代理模式能够提供许多优势,例如:虚拟、远程等,所以使用代理模式会更好一些

4. 代理是否能给自己添加任何功能?
答:代理可以向真实主题添加额外的功能,而无需更改对象的代码。代理和真实主题可以实现相同的接口

8. 小结

  1. 介绍了代理的概念,如何在软件架构中有效地使用它
  2. 探讨了代理模式及其使用的上下文
  3. UML类图,基于 Python3.x实现代理设计模式的实例
  4. 代理模式有4种不同的实现方式:虚拟代理、远程代理、保护代理和智能代理
  5. 门面模式和代理模式进行比较
  6. 常见解答,进一步了解代理模式背后的思想和优缺点

你可能感兴趣的:(Python,Full,Stack,Design,Patterns,Python3,Design,Patterns)