1. 什么是代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式通过引入一个代理对象来间接访问真实对象,从而在不改变真实对象的情况下,增加对其访问的控制和管理。
在软件开发中,直接访问某个对象可能会涉及到复杂的操作、资源管理或安全问题。例如,在处理大型对象时,可能不希望在程序启动时就加载所有数据,而是希望在真正需要时才进行加载。此外,某些对象可能需要进行权限检查、日志记录或其他操作,这些都可以通过代理模式来实现。
代理模式的主要作用包括:
-
控制访问:代理可以在访问真实对象之前进行权限检查或其他控制,确保只有合适的请求才能访问真实对象。
-
延迟加载:通过代理,可以在需要时才加载真实对象,从而节省资源。例如,在处理大型数据时,可以使用代理来延迟对象的创建,直到真正需要时才进行初始化。
-
记录日志:代理可以在调用真实对象的方法时记录日志,方便后续的调试和分析。这对于监控系统的行为和性能非常有帮助。
-
远程代理:在分布式系统中,代理可以代表远程对象进行操作,隐藏网络通信的复杂性。客户端可以像访问本地对象一样访问远程对象,从而简化了编程模型。
-
缓存:代理可以实现对真实对象的结果进行缓存,以提高性能。例如,在网络请求中,代理可以缓存之前的请求结果,避免重复的网络调用。
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"。只有管理员用户才能访问网络请求。 - 缓存和日志记录:与之前的示例相同,代理类在发送请求之前检查缓存,并在请求后记录日志。
- 权限检查 :在
- 客户端代码:创建两个代理对象,一个是管理员用户,另一个是访客用户。管理员用户可以成功获取数据,而访客用户则会被拒绝访问。