#代理模式-----控制对象的访问
代理通常就是一个介于寻求方和提供方之间的中介系统。寻求方是发出请求的一方,而提供方则是根据请求提供资源的一方。在Web中,它相当于代理服务器。客户端在向网站发出请求时,首先连接到代理服务器,然后向它请求诸如网页之类的资源。代理服务器在内部评估此请求,将其发送到适当的服务器,当它收到响应后,就会将响应传递给客户端。因此,代理服务器可以封装请求、保护隐私,并且非常适合在分布式架构中运行。
在设计模式的上下文中,代理是充当实际对象接口的类。对象类型可以是多样化的,例如网络连接、内存和文件中的大对象,等等。简而言之,代理就是封装实际服务对象的包装器或代理人。代理可以为其包装的对象提供附件功能,而无需更改对象的代码。代理模式的主要目的是为其他对象提供一个代理者或占位符,从而控制实际对象的访问。
代理模式可以用于多种场景,如下所示:
(1)它能够以更简单的方式表示一个复杂的系统。例如,涉及多个复杂计算或过程的系统应该提供一个更简单的接口,让它充当客户端的代理。
(2)它提高了现有的实际对象的安全性。在许多情况下,都不允许客户端直接访问实际对象。这是因为实际对象可能受到恶意活动的侵害。这时候,代理就能起到抵御恶意活动的盾牌作用,从而保护了实际的对象。
(3)它为消耗大量内存的对象提供了一个轻量级的句柄。有时,你可能不想加载主要对象,除非它们真的有必要。这是因为实际对象真的很笨重,可能需要消耗大量资源。例如,在网站的个人简介头像,最好在列表视图中显示头像的缩略图。
以演员和经纪人为例,当制作公司像要找演员拍电影时,他们通常会与经纪人交流,而不是直接跟演员交流。经纪人作为代理。
#演员:
class Actor:
def __init__(self):
self.isBusy = False
def occupied(self):
self.isBusy = True
print(type(self).__name__, 'is occupied with current movie')
def available(self):
self.isBusy = False
print(type(self).__name__, 'is free for the movie')
def getStatus(self):
return self.isBusy
#代理:
class Agent:
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()
代理设计模式主要完成了以下工作:
(1)它为其他对象提供了一个代理,从而实现了对原始对象的访问控制。
(2)它可以用作一个层或接口,以支持分布式访问。
(3)它通过增加代理,保护真正的组件不受意外的影响。
##一、代理模式的UML图
通过观察这个UML图,你会发现这个模式有下述3个主要的者:
- 代理:它维护一个引用,允许代理(Proxy)通过这个引用来访问实际对象。它提供了一个与主题(Subject)相同的接口,以便代理可以直接替换真实的主题。代理还负责创建和删除真实主题(RealSubject)。
- 主题:它定义了(RealSubject)和(Proxy)的公共接口。以Proxy 和 RealSubject 的形式实现主题(Subject) ,使用RealSubject的任何地方都可以使用代理(Proxy)。
- 真实主题:它定义代理(Proxy)所代表的真实对象。
从数据结构角度看,UML图可以表示如下:
- 代理:它是一个控制对RealSubject类访问的类。它代理客户端的请求,负责创建或删除RealSubject。
- 主题/真实主题:主题是定义真实主题(RealSubject)和代理(Proxy)相类似的接口。RealSubject是Subject接口的实际实现。它提供了真正的功能,然后有客户端使用。
- 客户端:它访问要完成工作的Proxy类。Proxy类在内部控制对RealSubject的访问,并引导客户端(Client)所请求的工作。
##二、不同类型的代理
(1)虚拟代理
如果一个对象实例化后会占用大量内存的话,可以先利用占位符来表示,这就是所谓的虚拟代理。例如,你想在网站上加载大型图片,而这个请求需要很长时间才能加载完成。通常,开发人员将在网页上创建一个占位符图标,以提示这里有图片。但是,只有当用户实际点击图标才会加载图像,从而节省了向存储器中加载大型图像的开销。因此,在虚拟代理中,当客户端请求或访问对象时,才会创建实际对象。
(2)远程代理
远程代理可表述如下:它给位于远程服务器或不同地址空间上的实际对象提供了一个本地表示。例如,你希望为应用程序建立一个监控系统,而该应用涉及多个Web服务器、数据库服务器、缓存服务器等。如果我们要监视这些服务器的CPU和磁盘利用率,就需要建立一个对象,该对象能够用于监视应用程序运行的上下文中,同时还可以执行远程命令以获取实际的参数值。在这种情况下,建立一个作为远程对象的本地表示的远程代理对象将可以帮助我们实现实现这个目标。
(3)保护代理
这种代理能够控制RealSubject的敏感对象的访问。例如,在当今分布式系统的世界中,Web应用会提供多个服务,这些服务相互协作来提供各种功能。现在,在这样的系统中,认证服务充当负责认证和授权的保护性代理服务器。在这种情况下,代理自然有助于保护网站的核心功能,防止无法识别或未授权的服务它们。因此,代理对象会检查调用者是否具有转发请求所需的访问权限。
(4)智能代理
智能代理在访问对象时插入其他操作。例如,假设在系统中有一个核心组件,它将状态信息集中保存在一个地点。通常情况下,这样的组件需要被多个不同的服务器调用以完成它们的任务,并且可能导致共享资源的问题。与让服务直接调用核心组件不同,智能代理是内置的,并且会在访问之前检查实际对象是否被锁定,以确保没有其他对象可以更改它。
##三、现实世界中的代理模式
假设,你要买一件衣服,但是手里的现金却不够了。现在只要在商家刷一下借记卡,这笔钱就会划入商家的账户,从而完成支付。
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 __hasFounds(self):
print('Bank:: Checking if Account', self.__getAccount(), 'has enough founds')
return True
def setCard(self, card):
self.card = card
def do_pay(self): #实现do_pay方法向商家付款
if self.__hasFounds():
print('Bank:: paying the merchant')
return True
else:
print('Bank:: sorry,not enough founds')
return False
#DebitCard类充当真实主题(Bank类)的代理:
class DebitCard(Payment):
def __init__(self): #调用代理并将其实例化
self.bank = Bank()
def do_pay(self):
card = input('Proxy: Punch in Card Number:')
self.bank.setCard(card)
return self.bank.do_pay()
#客户端:
class You:
def __init__(self):
print('You:: Lei\'s buy the shirt')
self.debitCard = DebitCard()
self.isPurchased = None
def make_payment(self): #调用代理的方法进行付款
self.isPurchased = self.debitCard.do_pay()
def __del__(self): #如果付款成功,将返回__del__()方法???
if self.isPurchased:
print('You:: buy successfully!')
else:
print('You::buy faild!')
if __name__ == '__main__':
you = You()
you.make_payment()
##四、代理模式的优点
- 代理可以通过缓存笨重的对象或频繁访问的对象来提高应用程序的性能。
- 代理还提供对于真实主题的访问授权。因此,只有提供合适权限的情况下,这个模式才会接受委派。
- 远程代理还便于与可用作网络连接和数据库连接的远程服务器进行交互,并可用于监视系统。
##五、门面模式和代理模式之间的比较
门面模式和代理模式都是结构型模式。相似之处在于,都是在真实对象的前面加入一个代理/门面对象。但是在意图或目的方面,这两种模式的确存在差异,如下:
代理模式 | 门面模式 |
---|---|
它为其他对象提供了代理或占位符,以控制对原始对象的访问 | 它为类的大型子系统提供了一个接口 |
代理对象具有与其目标对象相同的接口,并保存有目标对象的引用 | 它实现了子系统之间的通信和依赖性的最小化 |
它充当客户端和被封装的对象之间的中介 | 门面对象提供了单一的简单接口 |