Паттерн Proxy (Заместитель)
Паттерн Proxy (Заместитель) — это структурный паттерн проектирования, который предоставляет объект-заменитель или placeholder для другого объекта. Прокси контролирует доступ к оригинальному объекту, позволяя выполнять действия до или после обращения к нему.
Основные цели Proxy:
- Контроль доступа к объекту
- Добавление дополнительной логики перед/после обращения к объекту
- Ленивая инициализация (отложенное создание объекта)
- Кеширование результатов
- Удалённый доступ (например, для объектов в другом адресном пространстве)
Типы прокси:
- Virtual Proxy - откладывает создание ресурсоёмких объектов
- Protection Proxy - контролирует доступ к объекту
- Remote Proxy - представляет объект в другом адресном пространстве
- Smart Reference - добавляет дополнительную логику при обращении к объекту
- Caching Proxy - кеширует результаты запросов
Примеры на Python
1. Virtual Proxy (Ленивая инициализация)
class LazyImage:
def __init__(self, filename):
self._filename = filename
self._image = None
def display(self):
if self._image is None:
print(f"Loading image {self._filename}")
self._image = f"Image data for {self._filename}"
print(f"Displaying {self._filename}")
return self._image
# Использование
image = LazyImage("photo.jpg")
# Изображение ещё не загружено
print(image.display()) # Загружается и отображается
print(image.display()) # Только отображается (уже загружено)
2. Protection Proxy (Контроль доступа)
class SensitiveData:
def __init__(self):
self._data = "Top Secret Data"
def read(self):
return self._data
class DataProxy:
def __init__(self, user):
self._user = user
self._real_data = SensitiveData()
def read(self):
if self._user == "admin":
return self._real_data.read()
else:
return "Access Denied"
# Использование
admin_proxy = DataProxy("admin")
print(admin_proxy.read()) # "Top Secret Data"
user_proxy = DataProxy("user")
print(user_proxy.read()) # "Access Denied"
3. Remote Proxy (Удалённый доступ)
import json
from abc import ABC, abstractmethod
class DatabaseService(ABC):
@abstractmethod
def get_data(self, query):
pass
class RealDatabaseService(DatabaseService):
def get_data(self, query):
# В реальности здесь было бы подключение к БД
return {"result": f"Data for query: {query}"}
class DatabaseProxy(DatabaseService):
def __init__(self):
self._real_service = None
self._cache = {}
def get_data(self, query):
# Ленивая инициализация
if self._real_service is None:
print("Connecting to remote database...")
self._real_service = RealDatabaseService()
# Кеширование
if query in self._cache:
print("Returning cached result")
return self._cache[query]
result = self._real_service.get_data(query)
self._cache[query] = result
return result
# Использование
proxy = DatabaseProxy()
print(proxy.get_data("SELECT * FROM users")) # Соединение + запрос
print(proxy.get_data("SELECT * FROM users")) # Возврат из кеша
4. Smart Proxy (Дополнительная логика)
class BankAccount:
def __init__(self, balance=0):
self._balance = balance
def deposit(self, amount):
self._balance += amount
def withdraw(self, amount):
if amount > self._balance:
raise ValueError("Insufficient funds")
self._balance -= amount
def get_balance(self):
return self._balance
class BankAccountProxy:
def __init__(self, real_account, owner):
self._real_account = real_account
self._owner = owner
self._access_count = 0
def deposit(self, amount):
self._access_count += 1
print(f"Log: {self._owner} deposited {amount}")
self._real_account.deposit(amount)
def withdraw(self, amount):
self._access_count += 1
print(f"Log: {self._owner} tried to withdraw {amount}")
self._real_account.withdraw(amount)
def get_balance(self):
self._access_count += 1
print(f"Log: {self._owner} checked balance")
return self._real_account.get_balance()
def get_access_count(self):
return self._access_count
# Использование
account = BankAccount(100)
proxy = BankAccountProxy(account, "John Doe")
proxy.deposit(50)
proxy.withdraw(30)
print(f"Balance: {proxy.get_balance()}")
print(f"Access count: {proxy.get_access_count()}")
Преимущества Proxy:
- Контроль доступа к реальному объекту
- Дополнительные возможности без изменения реального объекта
- Ленивая инициализация ресурсоёмких объектов
- Кеширование результатов
- Упрощение работы с удалёнными объектами
Недостатки:
- Увеличение времени отклика из-за дополнительной логики
- Усложнение кода (введение дополнительных классов)
Паттерн Proxy особенно полезен, когда нужно добавить дополнительное поведение к объекту без изменения его кода или когда создание реального объекта является ресурсоёмкой операцией.