代理模式
在某些应用中,我们想要在访问某个对象之前执行一个或多个重要的操作,例如,访问敏感信息——在允许用户访问敏感信息之前,我们希望确保用户具备足够的权限。操作系统中也存在类似的情况,用户必须具有管理员权限才能在系统中安装新程序。
上面提到的重要操作不一定与安全问题相关。延迟初始化(请参考网页[t.cn/Ryf47bV])是另一个案例:我们想要把一个计算成本较高的对象的创建过程延迟到用户首次真正使用它时才进行。
这类操作通常使用代理设计模式(Proxy design pattern)来实现。该模式因使用代理(又名替代,surrogate)对象在访问实际对象之前执行重要操作而得其名。以下是四种不同的知名代理类型(请参考[GOF95,第234页]和网页[t.cn/RqrYEn9])。
远程代理:实际存在于不同地址空间(例如,某个网络服务器)的对象在本地的代理者。
虚拟代理:用于惰性求值,将一个大计算量对象的创建延迟到真正需要的时候进行。
保护/防护代理:控制对敏感对象的访问。
智能(引用)代理:在对象被访问时执行额外的动作。此类代理的例子包括引用计数和线程安全检查。
我发现虚拟代理非常有用,所以现在通过一个例子来看看可以如何实现它。在9.4节中将学习如何创建防护代理。
使用Python来创建虚拟代理存在很多方式,但我始终喜欢地道的/符合Python风格的实现。这里展示的代码源自网站stackoverflow.com用户Cyclone的一个超赞回答(请参考网页[t.cn/RqrYudC])。为避免混淆,我先说明一下,在本节中,术语特性(property)、变量(variable)、属性(attribute)可相互替代使用。我们先创建一个LazyProperty类,用作一个修饰器。当它修饰某个特性时,LazyProperty惰性地(首次使用时)加载特性,而不是立即进行。init方法创建两个变量,用作初始化待修饰特性的方法的别名。method变量是一个实际方法的别名,method_name变量则是该方法名称的别名。为更好理解如何使用这两个别名,可以将其值输出到标准输出(取消注释下面代码中的两个注释行)。
import time
class SalesManager:
def work(self):
print("Sales Manager working...")
def talk(self):
print("Sales Manager ready to talk")
class Proxy:
def __init__(self):
self.busy = 'No'
self.sales = None
def work(self):
print("Proxy checking for Sales Manager availability")
if self.busy == 'No':
self.sales = SalesManager()
time.sleep(2)
self.sales.talk()
else:
time.sleep(2)
print("Sales Manager is busy")
if __name__ == '__main__':
p = Proxy()
p.work()
p.busy = 'Yes'
p.work()
Proxy checking for Sales Manager availability
Sales Manager ready to talk
Proxy checking for Sales Manager availability
Sales Manager is busy
## 以下是来自于github的代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
class SalesManager:
def talk(self):
print("Sales Manager ready to talk")
class Proxy:
def __init__(self):
self.busy = 'No'
self.sales = None
def talk(self):
print("Proxy checking for Sales Manager availability")
if self.busy == 'No':
self.sales = SalesManager()
time.sleep(0.1)
self.sales.talk()
else:
time.sleep(0.1)
print("Sales Manager is busy")
class NoTalkProxy(Proxy):
def talk(self):
print("Proxy checking for Sales Manager availability")
time.sleep(0.1)
print("This Sales Manager will not talk to you whether he/she is busy or not")
if __name__ == '__main__':
p = Proxy()
p.talk()
p.busy = 'Yes'
p.talk()
p = NoTalkProxy()
p.talk()
p.busy = 'Yes'
p.talk()
### OUTPUT ###
#