DEV Community

架构师小白
架构师小白

Posted on

代理模式深度指南:控制对象访问的艺术

代理模式深度指南:控制对象访问的艺术

在软件开发中,我们经常需要控制对某个对象的访问。代理模式提供了一种优雅的解决方案,通过引入一个代理对象来间接访问真实对象,从而实现访问控制、延迟加载、功能增强等多种高级特性。

什么是代理模式?

代理模式(Proxy Pattern)是一种结构型设计模式,它为另一个对象提供一个替身或占位符,以控制对这个对象的访问。代理对象可以在不改变真实对象接口的前提下,执行额外的逻辑。

核心组成

  1. 抽象主题(Subject):真实对象和代理对象的共同接口
  2. 真实主题(Real Subject):被代理的真实对象
  3. 代理(Proxy):持有真实对象的引用,负责控制和管理访问

代理模式的类型

1. 远程代理

为位于不同地址空间的对象提供本地代表。

class RemoteProxy:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self._stub = None

    def _connect(self):
        if not self._stub:
            self._stub = self._create_stub()
        return self._stub

    def request(self, method, *args):
        stub = self._connect()
        return stub.invoke(method, *args)
Enter fullscreen mode Exit fullscreen mode

2. 虚代理

延迟加载大型对象,只在真正需要时才创建真实对象。

class VirtualProxy:
    def __init__(self, file_path):
        self.file_path = file_path
        self._real_object = None

    @property
    def real_object(self):
        if self._real_object is None:
            print("正在加载大型对象...")
            self._real_object = HeavyObject(self.file_path)
        return self._real_object

    def get_data(self):
        return self.real_object.get_data()
Enter fullscreen mode Exit fullscreen mode

3. 保护代理

控制对真实对象的访问权限。

class ProtectionProxy:
    def __init__(self, real_subject, user_role):
        self.real_subject = real_subject
        self.user_role = user_role

    def request(self):
        if self.user_role == "admin":
            return self.real_subject.request()
        return "权限不足,拒绝访问"
Enter fullscreen mode Exit fullscreen mode

4. 智能引用

在访问对象时执行额外的操作,如计数、缓存等。

class SmartProxy:
    def __init__(self, real_subject):
        self.real_subject = real_subject
        self.access_count = 0

    def request(self):
        self.access_count += 1
        print(f"对象被访问了 {self.access_count}")
        return self.real_subject.request()
Enter fullscreen mode Exit fullscreen mode

实际应用场景

场景一:图片懒加载

from typing import Optional

class Image:
    def __init__(self, path: str):
        self.path = path
        self._data: Optional[bytes] = None

    def load(self) -> bytes:
        if self._data is None:
            print(f"Loading image from {self.path}...")
            with open(self.path, rb) as f:
                self._data = f.read()
        return self._data

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

class ImageProxy:
    def __init__(self, path: str):
        self.path = path
        self._image: Optional[Image] = None

    def display(self):
        if self._image is None:
            self._image = Image(self.path)
        self._image.display()

# 使用
proxy = ImageProxy("photo.jpg")
proxy.display()  # 首次显示时才加载
Enter fullscreen mode Exit fullscreen mode

场景二:API 限流保护

import time
from collections import deque

class RateLimiter:
    def __init__(self, max_requests: int, window_seconds: int):
        self.max_requests = max_requests
        self.window_seconds = window_seconds
        self.requests = deque()

    def allow(self) -> bool:
        now = time.time()
        while self.requests and self.requests[0] < now - self.window_seconds:
            self.requests.popleft()

        if len(self.requests) < self.max_requests:
            self.requests.append(now)
            return True
        return False

class APIService:
    def __init__(self):
        self.data = {"users": [1, 2, 3]}

    def get_users(self):
        return self.data["users"]

class APIServiceProxy:
    def __init__(self, service: APIService, limiter: RateLimiter):
        self.service = service
        self.limiter = limiter

    def get_users(self):
        if self.limiter.allow():
            return self.service.get_users()
        raise Exception("Rate limit exceeded")
Enter fullscreen mode Exit fullscreen mode

场景三:数据库连接池

class DatabaseConnection:
    def __init__(self):
        print("创建新的数据库连接...")
        time.sleep(0.1)

    def query(self, sql: str):
        return f"Result: {sql}"

class ConnectionPool:
    def __init__(self, max_size: int = 5):
        self.max_size = max_size
        self.available = []
        self.in_use = []

    def acquire(self) -> DatabaseConnection:
        if self.available:
            conn = self.available.pop()
        elif len(self.in_use) < self.max_size:
            conn = DatabaseConnection()
        else:
            raise Exception("No available connections")
        self.in_use.append(conn)
        return conn

    def release(self, conn: DatabaseConnection):
        if conn in self.in_use:
            self.in_use.remove(conn)
            self.available.append(conn)
Enter fullscreen mode Exit fullscreen mode

代理模式 vs 装饰器模式

特性 代理模式 装饰器模式
目的 控制访问 动态添加功能
关系 代理与真实对象通常有相同接口 装饰器包装对象
创建时机 可在运行时创建真实对象 通常在设计时确定
透明性 对客户端透明 透明添加功能

总结

代理模式是一种强大的设计模式,它能够帮助我们:

  • 控制访问:实现权限验证、限流等
  • 延迟加载:提高系统性能
  • 功能增强:在访问前后执行额外操作
  • 解耦:分离访问逻辑和业务逻辑

在实际开发中,代理模式广泛用于缓存、远程调用、懒加载、事务管理等场景。理解代理模式,能够帮助你设计出更加优雅和高效的架构。


推荐阅读

  • 《设计模式》 - Erich Gamma 等著
  • Clean Code - Robert C. Martin 著

如果你喜欢这篇文章,欢迎关注我的更多内容!🚀

Top comments (0)