Python代理模式介绍、使用

一、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语句抛出一个异常对象,从而使得程序进入异常处理流程。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2023年11月9日
下一篇 2023年11月9日

相关推荐