【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类图

代理模式的Uml类图

我们发现这个模式有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. 常见解答,进一步了解代理模式背后的思想和优缺点
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验题目 编写一个类实现银行帐户的概念,包括的属性有“帐号”、“储户姓名”、“地址”、“存款余额”,包括的方法有“存款”、“取款”、“查询”、“计算利息”、“累加利息”等。 实验要求 改写上面的类,增加一个类的静态属性“最小余额”和一个用来修改这个最小余额属性的方法。 改写上面的类,增加一个类的静态属性“活期利率”和封装这个属性的相应方法。 程序模块 实验后的结果验证 总结体会 通过此次实验,我初步掌握了编写一个类的概念,更进一步的熟悉了static等等与之用法相类似的语法类,增强了自己学好JAVA的信心。进一步掌握静态对象和非静态对象的区别与联系。 静态对象的数据在全局是唯一的,一改都改。如果你想要处理的东西是整个程序中唯一的,弄成静态是个好方法。 非静态的东西你修改以后只是修改了他自己的数据,但是不会影响其他同类对象的数据。 静态对象和非静态对象都是对象,是对象都是要实例化的。不同之处就是2者的数据表现和存储方式不一样。 静态的好处: 引用方便。对于公用类型的变量,直接用 类名.静态方法名 或者 类名.静态变量名就可引用并且直接可以修改其属性值,不用getter和setter方法。 保持数据的唯一性。此数据全局都是唯一的,修改他的任何一处地方,在程序所有使用到的地方都将会体现到这些数据的修改。 有效减少多余的浪费。
要求编写程序模拟银行账户的存、取款操作。按要求完成以下步骤: 步骤1:编写程序Account.java,其中定义银行账户类Account。该类中有账号、姓名、 存款余额等数据域,余额默认是0;有存款、取款、获取当前余额等方法。其中账号为长度 为12位数字的字符串,姓名为字符串,存款余额为double。 步骤2:编写名为CreditAccount类的信用卡账户类。该类继承自Account类,增加一 个透支限额(double)数据域,透支限额默认为1000。同时该类账户取款时允许透支,但不 能超过透支限额。 步骤3:编写名为SavingAccount的储蓄账户类SavingAccount。该类继承自Account 类。该类账户取款时不允许透支。 步骤4:编写名为Bank的模拟银行类,其中可以存储多个类型可能是信用卡账户或储 蓄账户的对象(可以用数组或ArrayList实现)。该类包含以下方法: 开户:即增加一个新的账户,注意:不允许两个账户的账号相同 销户:即删除一个已有的账户 查询账户:根据一个账号,查询有无该账号的账户 统计目前银行的存款总余额的方法。 统计目前银行的信用卡账户总透支金额的方法。 统计目前总账户数。 统计目前信用卡账户数 统计目前储蓄卡账户数 步骤5:编写客户端类Client.java完成以下功能: 编写一个静态方法,创建一个银行对象,并随机生成10个账号从1000 0000 0000 0000到1000 0000 0000 0009,类型不同的账户。 main方法中模拟几次开户操作。 main方法中模拟几次销户操作。 模拟几个对指定账号的存款和取款操作。 输出银行的总账户数、总余额、总透支数、各类具体账户数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值