Python设计模式:代理模式

1. 什么是代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式通过引入一个代理对象来间接访问真实对象,从而在不改变真实对象的情况下,增加对其访问的控制和管理。

在软件开发中,直接访问某个对象可能会涉及到复杂的操作、资源管理或安全问题。例如,在处理大型对象时,可能不希望在程序启动时就加载所有数据,而是希望在真正需要时才进行加载。此外,某些对象可能需要进行权限检查、日志记录或其他操作,这些都可以通过代理模式来实现。

代理模式的主要作用包括:

  1. 控制访问:代理可以在访问真实对象之前进行权限检查或其他控制,确保只有合适的请求才能访问真实对象。

  2. 延迟加载:通过代理,可以在需要时才加载真实对象,从而节省资源。例如,在处理大型数据时,可以使用代理来延迟对象的创建,直到真正需要时才进行初始化。

  3. 记录日志:代理可以在调用真实对象的方法时记录日志,方便后续的调试和分析。这对于监控系统的行为和性能非常有帮助。

  4. 远程代理:在分布式系统中,代理可以代表远程对象进行操作,隐藏网络通信的复杂性。客户端可以像访问本地对象一样访问远程对象,从而简化了编程模型。

  5. 缓存:代理可以实现对真实对象的结果进行缓存,以提高性能。例如,在网络请求中,代理可以缓存之前的请求结果,避免重复的网络调用。

2. 示例 1:图像加载代理

在实际开发中,图像加载是一个常见的场景,尤其是在图形用户界面(GUI)应用程序中。加载高分辨率图像可能会消耗大量的内存和时间,因此我们可以使用代理模式来实现延迟加载。

python 复制代码
class RealImage:
    def __init__(self, filename):
        self.filename = filename
        self.load_image_from_disk()

    def load_image_from_disk(self):
        print(f"Loading {self.filename}")

    def display(self):
        print(f"Displaying {self.filename}")


class ProxyImage:
    def __init__(self, filename):
        self.filename = filename
        self.real_image = None

    def display(self):
        if self.real_image is None:
            self.real_image = RealImage(self.filename)  # 延迟加载
        self.real_image.display()


if __name__ == "__main__":
    # 创建代理对象
    proxy_image = ProxyImage("test_image.jpg")

    # 第一次调用 display 方法时,图像会被加载
    proxy_image.display()

    # 再次调用 display 方法时,图像不会被重新加载
    proxy_image.display()
python 复制代码
Loading test_image.jpg
Displaying test_image.jpg
Displaying test_image.jpg

3. 示例 2:网络请求代理

在现代应用程序中,网络请求是一个常见的场景。为了提高性能和安全性,我们可以使用代理模式来管理网络请求。以下是一个复杂的示例,展示如何使用代理模式来实现网络请求的缓存和日志记录。

python 复制代码
import requests
import time
import json

# 真实主题(Real Subject)
class RealNetworkRequest:
    def __init__(self, url):
        self.url = url

    def fetch_data(self):
        print(f"Fetching data from {self.url}")
        response = requests.get(self.url)
        return response.json()

# 代理(Proxy)
class ProxyNetworkRequest:
    def __init__(self, url, user_role):
        self.url = url
        self.real_request = RealNetworkRequest(url)
        self.cache = {}
        self.log_file = "request_log.txt"
        self.user_role = user_role  # 用户角色

    def fetch_data(self):
        # 权限检查
        if not self.check_access():
            print("Access denied: You do not have permission to access this resource.")
            return None

        # 检查缓存
        if self.url in self.cache:
            print("Returning cached data.")
            return self.cache[self.url]

        # 记录请求开始时间
        start_time = time.time()

        # 发送请求
        data = self.real_request.fetch_data()

        # 将数据存入缓存
        self.cache[self.url] = data

        # 记录请求结束时间
        end_time = time.time()
        self.log_request(start_time, end_time)

        return data

    def check_access(self):
        # 简单的权限检查逻辑
        # 这里可以根据实际需求进行更复杂的权限检查
        return self.user_role == "admin"  # 只有 admin 用户可以访问

    def log_request(self, start_time, end_time):
        duration = end_time - start_time
        log_entry = f"Requested {self.url} in {duration:.2f} seconds\n"
        with open(self.log_file, "a") as log:
            log.write(log_entry)

# 客户端代码
if __name__ == "__main__":
    url = "https://jsonplaceholder.typicode.com/posts/1"
    
    # 创建代理对象,用户角色为 "admin"
    proxy_request = ProxyNetworkRequest(url, user_role="admin")

    # 第一次调用 fetch_data 方法时,数据会被请求并缓存
    data = proxy_request.fetch_data()
    print("Data:", json.dumps(data, indent=2))

    # 再次调用 fetch_data 方法时,数据将从缓存中返回
    data = proxy_request.fetch_data()
    print("Data:", json.dumps(data, indent=2))

    # 创建代理对象,用户角色为 "guest"
    proxy_request_guest = ProxyNetworkRequest(url, user_role="guest")

    # 尝试调用 fetch_data 方法,应该会被拒绝
    data = proxy_request_guest.fetch_data()
    print("Data:", json.dumps(data, indent=2))
python 复制代码
Fetching data from https://jsonplaceholder.typicode.com/posts/1
Data: {
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
Returning cached data.
Data: {
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
Access denied: You do not have permission to access this resource.
Data: null
  • 真实主题(RealNetworkRequest):负责发送网络请求并获取响应。
  • 代理(ProxyNetworkRequest)
    • 权限检查 :在 fetch_data 方法中添加了 check_access 方法,检查用户角色是否为 "admin"。只有管理员用户才能访问网络请求。
    • 缓存和日志记录:与之前的示例相同,代理类在发送请求之前检查缓存,并在请求后记录日志。
  • 客户端代码:创建两个代理对象,一个是管理员用户,另一个是访客用户。管理员用户可以成功获取数据,而访客用户则会被拒绝访问。
相关推荐
誉鏐17 分钟前
PyTorch复现线性模型
人工智能·pytorch·python
胡斌附体30 分钟前
qt socket编程正确重启tcpServer的姿势
开发语言·c++·qt·socket编程
白露与泡影1 小时前
Java面试题及答案整理( 2025年 4 月最新版,持续更新)
java·开发语言
YueiL1 小时前
C++入门练习之 给出年分m和一年中的第n天,算出第n天是几月几号
开发语言·c++·算法
冷凝女子1 小时前
【QT】获取文件路径中的文件名,去掉后缀,然后提取文件名中的数字
开发语言·数据库·qt
weixin_435208161 小时前
通过 Markdown 改进 RAG 文档处理
人工智能·python·算法·自然语言处理·面试·nlp·aigc
我不想当小卡拉米1 小时前
C++:继承+菱形虚拟继承的一箭双雕
开发语言·jvm·c++
weixin_457885821 小时前
JavaScript智能对话机器人——企业知识库自动化
开发语言·javascript·自动化
东方佑1 小时前
利用Python自动化处理PPT样式与结构:从提取到生成
python·自动化·powerpoint
孤独得猿2 小时前
Qt常用控件第一部分
服务器·开发语言·qt