- UML类图简介
- 设计模式的分类
- 面向对象的设计原则
- python设计模式【1】-单例模式
- python设计模式【2】-工厂模式
- python设计模式【3】-门面模式
- python设计模式【4】-代理模式
- python设计模式【5】-观察者模式
- python设计模式【6】-命令模式
- python设计模式【7】-模板方法模式
- python设计模式【8】-模型·视图·控制器-复合模式
- python设计模式【9】-状态模式
理解代理模式
代理通常就是一个介于寻求方和需求方之间的中介系统。寻求方是发出请求的一方,而提供方是根据请求提供资源的一方。在Web世界中,它相当于代理服务器。客户端在向网站发出请求时,首先连接到代理服务器,然后向它请求诸如网页之类的资源。代理服务器在内部评估此请求,将其发送到适当的服务器,当它收到响应后,就会将响应传递给客户端。因此代理服务器可以封装请求、保护隐私,并且非常适合在分布式架构中运行。
在设计模式的上下文中,代理是充当实际对象接口的类。对象类型可以是多样化的,例如网络连接、内存和文件中的大对象;简而言之,代理就是封装实际服务对象的包装器或代理人。代理可以为其包装的对象提供附加功能,而无需更改对象的代码。代理模式的主要目的是为其他对象提供一个代理者或占位者,从而控制对实际对象的访问。
代理模式可以用于多种场景,如下所示:
1、它能够以简单的方式表示一个复杂的系统。例如,涉及多个复杂计算或过程的系统应该提供一个更简单的接口,让它充当客户端的代理。
2、他提高了现有的实际对象的安全性。在许多情况下,都不允许客户端直接访问实际对象。这是因为实际对象可能受到恶意活动的危害。这时候,代理就能起到抵御恶意活动的盾牌作用,从而保护了实际的对象。
3、它为不同服务器上的远程对象提供本地接口。一个明显的例子是客户端希望在远程系统上运行某些命令的分布式系统,但是客户端可能没有直接的权限来实现这一点。因此它将请求转交给本地对象(代理),然后有远程机器上的代理执行该请求。
4、它为大量消耗内存的对象提供一个轻量级的句柄。有时,可能不想加载主要对象,除非它们真的有必要。这是因为实际对象真的很笨重,可能需要消耗大量资源。一个典型的例子是网站用户的个人简介头像。最好在列表视图中显示简介头像的缩略图,当然,为了展示用户简介的详细介绍,就需要加载实际图片
下面我们通过生活中的一个例子来理解该模式。以演员与他的经纪人为例,当制作公司想要找演员拍电影时,它们通常会与经纪人交流,而不是直接跟演员交流。经纪人会根据演员的日程安排和其他的合约情况,来答复制作公司该演员是否有空,以及是否对该影片感兴趣。在这种情况下,制作公司并不直接找演员交涉,而是通过经纪人作为代理,处理所有与演员有关的调度和片酬问题。
class Actor(object):
def __init__(self):
self.is_busy = False
def occupied(self):
self.is_busy = True
print(type(self).__name__, "正在忙着")
def available(self):
self.is_busy = False
print(type(self).__name__, "有空")
def get_status(self):
return self.is_busy
class Agent(object):
def __init__(self):
self.principal = False
def work(self):
self.actor = Actor()
if self.actor.get_status():
self.actor.occupied()
else:
self.actor.available()
if __name__ == '__main__':
r = Agent()
r.work()
上面代码实现了这种场景,其中Actor是代理对象Agent用于查看Actor是否处于忙碌状态。如果Actor正忙,则调用Actor().occupied()方法;如果Actor不忙,则返回Actor().available()方法。
代理设计模式主要完成了以下工作:
1、它为其他对象提供了一个代理,从而实现了对原始对象的访问控制
2、它可以用作一个层或接口,以支持分布式访问
3、它通过增加代理,保护真正的组件不受意外的影响
不同类型的代理
根据代理的使用方式,我们可以将它们分为虚拟代理、远程代理、保护代理和智能代理
如果一个对象实例化后会占用大量内存的话,可以先利用占位符来表示,这就是所谓的虚拟代理。例如,假设你想在网站上加载大型图片,而这个请求需要很长时间才能加载完成。通常,开发人员将在网页上创建一个占位符图标,以提示这里有图像。但是,只有当用户实际点击图标时才会加载图像,从而节省了向存储器中加载大型图像的开销。因此,在虚拟代理中,当客户端请求或访问对象时,才会创建实际对象。
它给位于远程服务器或不同地址空间上的实际对象提供了一个本地表示。例如,你希望为应用程序建立一个监控系统,而该应用涉及多个Web服务器、数据库服务器、celery任务服务器、缓存服务器,等等。如果我们要监视这些服务器的CPU和磁盘利用率,就需要建立一个对象,该对象能够用于监视应用程序运行的上下文中,同时还可以执行远程命令以获取实际的参数值。在这种情况下,建立一个作为远程对象的本地表示的远程代理对象将可以帮助我们实现这个目标。
这种代理能够控制RealSubject(真实主题)的敏感对象的访问。例如,在当今分布式系统的世界中,web会提供多个服务,这些服务相互协作来提供各种功能。现在,在这样的系统中,认证服务充当负责认证和授权的保护性代理服务器。在这种情况下,代理自然有助于保护网站的核心功能,防止无法识别或未授权的代理访问它们。因此,代理对象会检查调用者是否具有转发请求所需的访问权限。
智能代理在访问对象时插入其他操作。例如。假设在系统中有一个核心组件,它将状态信息集中保存在一个地点。通常情况下,这样的组件需要被多个不同的服务调用以完成它们的任务,并且可能导致共享资源的问题。让服务之间调用核心组件不同,智能代理是内置的,并且会在访问之前检查实际对象是否被锁定,以确保没有其他对象可以更改它。
生活中的代理模式
我们将通过付款用例来展示代理模式的生活中的应用场景。假设你在商场看中了一件衣服,你想买但是手里的现金却不够了。但是你可以刷卡,这笔钱就会划入商家的账户,从而完成支付。
下面我们利用python来开发一个应用程序,实现上面的例子。首先从客户端开始:去了商场,想买一件衣服。
1、你的行为由类You表示;
2、为了购买衣服,该类提供了make_payment()方法;make_payment()方法在内部调用代理的方法进行付款。
3、特殊方法__init__()会调用代理并将其实例化;
3、如果付款成功,将返回__del__()方法
代码如下:
class You(object):
def __init__(self):
print("买衣服")
self.debit_card = DebitCard()
self.is_purchased = None
def make_payment(self):
self.is_purchased = self.debit_card.do_pay()
def __del__(self):
if self.is_purchased:
print("买到了")
else:
print("没买到")
you = You()
you.make_payment()
下面是主题,主题是由代理和真实主题实现的接口
1、在这个例子中,主题是Payment类,它是一个抽象基类,代表一个接口
2、付款具有一个do_pay()方法,该方法需要借助代理和真实主题来实现。
代码如下:
from abc import ABCMeta, abstractmethod
class Payment(metaclass=ABCMeta):
@abstractmethod
def do_pay(self):
pass
在这个场景中,我还开发了代表真实主题的Bank类;
1、Bank实际上完成从你账户向商家账户划账的工作。
2、Bank提供了多个方法来处理付款。代理使用set_card()方法将借记卡详细信息发送给银行。
3、__get_account()方法是Bank的私有方法,用于获取借记卡持有人的账号详细信息。
4、Bank还有__has_funds()方法,它用来查看账户持有人在账户中是否有足够的资金来购买衣服。
5、由Bank类(通过Payment接口)实现的do_pay()方法实际上负责可用资金向商家付款。
class Bank(Payment):
def __init__(self):
self.card = None
self.account = None
def __get_account(self):
self.account = self.card
return self.account
def __has_funds(self):
print("检查账户有足够的资金", self.__get_account())
return True
def set_card(self, card):
self.card = card
def do_pay(self):
if self.__has_funds():
print("向商家付款")
return True
else:
print("没有足够的资金")
return False
现在来理解最后一部分,即与代理有关的部分。
1、DebitCard类是此处的类。当你像付款时,它会调用do_play()方法
2、DebitCard类充当真实主题的代理
3、pay_with_card()方法在内部控制真实主题(Bank类)对象的创建,并向银行提供借记卡的详细信息。
4、Bank在内部对帐户进行检查并完成支付。
代码如下:
class DebitCard(Payment):
def __init__(self):
self.bank = Bank()
def do_pay(self):
card = input("请输入你的卡号:")
self.bank.set_card(card)
return self.bank.do_pay()
代理模式的优点
1、代理模式可以通过缓存笨重的对象或频繁访问的对象来提高应用程序的性能
2、代理还提供对于真实主题的访问授权。因此,只有提供合适权限的情况下,这个模式才会接受委派
2、远程代理还便于与可用作网络连接和数据库连接的远程服务器进行交互,并可用于监视系统。
门面模式和代理模式之间的比较
代理模式 | 门面模式 |
它为其他对象提供了代理或占位符,以控制对原始对象的访问 | 它为类的大型子系统提供了一个接口 |
代理对象具有与其目标对象相同的接口,并保存有目标对象的引用 | 它实现可子系统之间的通信和依赖性的最小化 |
它充当客户端和被封装的对象之间的中介 | 门面对象提供了单一的简单接口 |
参考:
《python设计模式》(第2版)https://www.epubit.com/