一、Python代理模式介绍
Python代理模式(Proxy Pattern)是一种结构型设计模式。在代理模式中,代理对象充当了另一个对象的占位符,以控制对该对象的访问。
代理对象和被代理对象实现了相同的接口,因此它们可以互相替代。客户端和代理对象之间的交互是无缝的,因为它们的接口是一样的。
代理模式的主要功能是为其他对象提供一个代理,以控制对对象的访问。代理对象可以在调用被代理对象之前或之后执行一些操作,例如身份验证,缓存等。
优点:
- 保护了真实对象的访问,可以对访问进行限制和控制;
- 可以提高访问效率,通过代理对象可以缓存数据或者调用其他服务等;
- 可以提高系统的灵活性,因为代理对象可以在不影响真实对象的情况下扩展其功能。
缺点:
- 可能引入额外的复杂性,因为需要创建代理对象。
- 此外,如果代理对象没有正确实现与真实对象相同的接口,可能会导致客户端代码无法正常工作。
应用场景:
- 身份验证:在访问某些敏感数据或操作时,可以使用代理对象执行身份验证,以确保用户有权访问该数据或执行该操作。
- 缓存:使用代理对象来缓存数据,以便在下一次访问时可以更快地访问数据。
- 远程服务:代理对象可以用作远程服务的本地代表,在使用远程服务时提供更好的用户体验,同时也可以提高访问效率。
代理模式可以通过组合或继承实现。通常,代理对象会继承与真实对象相同的接口,并在继承的方法中调用真实对象的方法。
二、代理模式使用
工作原理:
- 客户端代码通过代理对象调用真实对象。
- 代理对象执行一些额外的操作(例如身份验证或缓存),然后调用真实对象的方法。
- 真实对象执行所请求的操作,并将结果返回给代理对象。
- 代理对象将结果返回给客户端代码。
示例一:实现缓存功能
假设我们正在开发一个网络应用程序,该应用程序可以向外提供图片资源。由于图片资源较大,我们希望通过代理模式来缓存这些数据,以提高程序的性能和响应速度。
下面是一个基于Python的实现示例:
from abc import ABC, abstractmethod
# 定义抽象基类
class Image():
@abstractmethod
def display(self):
pass
# 定义具体子类,继承Image,重新display方法
class RealImage(Image):
def __init__(self, filename): # 构造函数,接收一个参数filename
self.filename = filename # 文件名保存在filename实例变量中
self.load_from_disk() # 调用load_from_disk()方法,从磁盘中加载图片数据
'''
从磁盘中加载图片数据
子类RealImage独有的方法
由于这个过程比较耗时,因此我们需要在初始化时进行加载,避免在图片显示时等待。
'''
def load_from_disk(self): #
print("loading " + self.filename)
# 显示图片
def display(self):
print("Displaying " + self.filename)
# 定义代理类,继承Image,重写display方法
class ImageProxy(Image):
def __init__(self, filename):
self.filename = filename
# 定义真实图片
self.real_image = None
def display(self):
if self.real_image is None:# 如果没有加载真实图片
# 调用具体子类RealImage, 创建真实图片对象, 缓存真实对象,避免重复加载图片资源
self.real_image = RealImage(self.filename)
# 显示真实图片
self.real_image.display()
image = ImageProxy("test.jpg")
print("第一次调用display方法:图片没有加载真实图片,调用子类RealImage, 创建真实图片对象")
image.display()
print("第二次调用display方法: 直接从缓存中获取图片")
image.display()
运行结果:
第一次调用display方法:图片没有加载真实图片,调用子类RealImage, 创建真实图片对象
loading test.jpg
Displaying test.jpg
第二次调用display方法: 直接从缓存中获取图片
Displaying test.jpg
在上面的示例中,我们定义了一个抽象基类Image
,其中包含一个display()
抽象方法。接着,我们定义了一个具体子类RealImage
,它代表了真实的图片对象,负责从磁盘中加载图片数据,并提供display()
方法用于显示图片。
我们还定义了一个代理类ImageProxy
,它也实现了Image
接口。当客户端调用display()
方法时,代理类会检查是否已经加载了真实的图片对象。如果没有,代理会先创建一个真实的图片对象,然后再调用它的display()
方法。这样,代理对象就可以缓存真实对象,避免了重复加载图片资源,从而提高程序性能。
使用代理模式时,客户端代码只需要与代理对象交互,并不需要知道真实对象的存在。当代理对象需要访问真实对象时,它会自动创建并调用真实对象,从而实现对真实对象的间接访问。
示例二:实现身份验证功能
在Python中,使用代理模式实现身份验证功能可以参考以下示例代码:
# 定义抽象主题类
class AbstractSubject():
def request(self):
pass
# 定义子类真实主题对象,继承抽象主题类
class RealSubject(AbstractSubject):
def request(self):
print("RealSubject: 处理请求...")
# 定义代理类, 继承抽象主题类
class Proxy(AbstractSubject):
def __init__(self, realsubject: RealSubject, username: str, password: str):
self._subject = realsubject
self._username = username
self._password = password
def request(self):
if self.authenticdate():
self._subject.request() # 调用子类request方法
else:
print("代理认证失败")
def authenticdate(self):
# 模拟身份认证过程
if self._username == "admin" and self._password == "admin123456":
print("代理认证成功")
return True
else:
return False
# 创建真实主题对象
real_subject = RealSubject()
# 创建代理对象
proxy = Proxy(real_subject, "admin", "admin123456")
# 通过代理对象发送请求
proxy.request()
# 修改代理认证信息,再次发送请求
proxy._username = "a"
proxy._password = "b"
proxy.request()
运行结果:
代理认证成功
RealSubject: 处理请求…
代理认证失败
在上述示例代码中,我们定义了一个抽象主题类AbstractSubject
和其子类RealSubject
,并在代理类Proxy
中持有一个真实主题对象_subject
。在客户端调用时,通过代理对象proxy
发送请求。
在代理类中,我们重写了request()
方法,在其中添加了身份验证的逻辑。如果身份验证成功,则代理对象将请求转发给真实主题对象;否则,代理对象将输出代理认证失败的消息。
代理类Proxy
的构造函数包含三个参数:
realsubject
参数是真实主题对象,使用类型提示指定为RealSubject
类。username
参数是用于身份认证的用户名,使用类型提示指定为字符串类型。password
参数是用于身份认证的密码,使用类型提示指定为字符串类型。
在代理类的构造函数中,将输入的真实主题对象、用户名和密码保存到对应的成员变量中,以便后续使用。
在客户端调用过程中,我们创建了一个真实主题对象real_subject
和代理对象proxy
,并通过代理对象发送请求。我们可以通过修改代理认证信息,测试代理类的身份验证功能是否正常工作。
示例三:实现远程服务功能
以下是一个示例代码,演示了如何使用 Python 代理模式实现远程服务功能:
from abc import ABC, abstractmethod
# 定义抽象类:远程服务接口
class RemoteService():
@abstractmethod
def request(self, param: str) -> str: # 抽象方法:接受字符串类型参数,返回字符串类型结果
pass
# 实现远程服务
class RemoteServiceIml(RemoteService):
def request(self, param: str) -> str:
return f"Remote service received {param}"
# 实现代理类
class RemoteServiceProxy(RemoteService):
def __init__(self, remote_service: RemoteService):
self._remote_service = remote_service
def request(self, param: str) -> str:
print("Remote service aouthentication...")# 调用远程服务器前,本地操作...,例如:身份认证、参数校验
if not param:
raise ValueError("Missing parameter")
# 调用远程服务,返回结果
return self._remote_service.request(param)
remote_service_impl = RemoteServiceIml()
proxy = RemoteServiceProxy(remote_service_impl)
res = proxy.request("test")
print(res)
运行结果:
Remote service aouthentication…
Remote service received test
在上述代码中,我们首先定义了一个远程服务接口RemoteService
,其中有一个request
方法,用于处理远程服务请求,返回一个字符串类型的结果。同时,我们还实现了RemoteServiceImpl
类,该类用于实现具体的远程服务逻辑。在具体的request
方法中,简单地将接收到的参数拼接成一条消息,并返回。
接下来,我们使用代理模式实现了RemoteServiceProxy
类,该类也实现了RemoteService
接口,并接收一个RemoteService
实例作为构造函数参数。当我们调用request
方法时,代理类先进行身份认证、参数校验等本地操作,然后再调用远程服务,最后将结果返回。
在使用示例中,我们先创建了RemoteServiceImpl
的实例,然后将它传入代理类的构造函数中创建代理对象proxy
。最后,我们调用代理对象的request
方法,传入一个字符串参数,并将返回结果打印出来。这个例子展示了代理模式如何在应对远程服务时起到的作用。
def request(self, param: str) -> str:解释:
这段代码是一个抽象方法的定义,其中request
是一个接受一个字符串类型的参数,返回一个字符串类型的结果的抽象方法,但是该方法没有具体实现,只是提供了一个方法头。这种方法被称为抽象方法,是一种在抽象类中定义,需要在子类中具体实现的方法。在Python中,我们可以通过定义抽象类和使用abstractmethod
装饰器来实现抽象方法的定义。子类必须实现抽象类中的所有抽象方法才能被实例化,并且子类中的抽象方法也可以再次定义为抽象方法,以继续在更深层次上实现多态行为。
raise ValueError("Missing parameter")解释:
这段代码是一个异常抛出语句,当代码执行到此处时,会抛出一个ValueError
类型的异常,并传递一个字符串参数”Missing parameter”作为异常信息。异常机制是Python中的一种错误处理机制,当程序出现错误时,可以使用异常机制提前中止程序的运行,并在异常发生的地方引发一个异常对象,这样就可以将错误信息传递给上层调用的代码,以便于更好地处理错误。在Python中,可以使用raise
语句抛出一个异常对象,从而使得程序进入异常处理流程。
文章出处登录后可见!