Skip to content

Instantly share code, notes, and snippets.

@DanyaIzm
Created February 13, 2024 19:24
Show Gist options
  • Save DanyaIzm/1227f2f0949499d8e60a401f527d8acd to your computer and use it in GitHub Desktop.
Save DanyaIzm/1227f2f0949499d8e60a401f527d8acd to your computer and use it in GitHub Desktop.
Упрощённая реализация паттерна "Декоратор"
from typing import Protocol
from dataclasses import dataclass
# Для примера представим, что мы пишем сервис, работающий с пользователями
@dataclass
class User:
name: str
age: int
hobbies: list[str]
# Создадим интерфейс для репозитория пользователя
class UserRepository(Protocol):
def get_user_by_name(self, name: str) -> User:
# У него будет метод получения пользователя по имени
...
# Напишем реализацию репозитория, которая будет удовлетворять интерфейсу выше
class PostgreSQLUserRepository:
# Пусть это будет SQL репозиторий
def __init__(self, connection: str) -> None:
self._connection = connection
# Напишем реализацию нашего метода
def get_user_by_name(self, name: str) -> User:
# Представим, что она возвращает нам пользователя из базы данных
return User(name, 42, ["программирование", "музыка"])
# Создадим класс декоратора, который будет выводить лог в
# процессе получения данных о пользователе.
class UserRepositoryWithLogging:
# В конструкторе в нашем декораторе мы будем принимать реализацию репозитория
def __init__(self, user_repo: UserRepository) -> None:
self._user_repo = user_repo
# Напишем реализацию метода, который будет делегировать всю основную работу
# нашему реальному репозиторию, попутно записывая подробный лог
# Обратите внимания, название, сигнатура и возвращаемое значение
# совпадают с интерфейсом и нашей SQL реализацией выше
def get_user_by_name(self, name: str) -> User:
print(f"[Logger] Производится поиск пользователя с именем {name}")
user = self._user_repo.get_user_by_name(name)
print("[Logger] Поиск отработал без ошибок")
print(f"[Logger] Был получен пользователь {user}")
return user
# Создадим функцию, которая будет принимать любую реализацию интерфейса репозитория
# и вызывать у неё метод get_user_by_name
def find_user(user_repo: UserRepository, name: str) -> User:
return user_repo.get_user_by_name(name)
def main() -> None:
# Создадим наш Postgre репозиторий
user_repo = PostgreSQLUserRepository("postgresql://localhost:5432/awesome")
# Вызовем функцию find_user, используя наш репозиторий.
# Заметьте, что функция принимает UserRepository, а мы передаём
# ей PostgreSQLUserRepository
print(find_user(user_repo, "Awesome"))
# Обернём наш репозиторий так, чтобы при поиске пользователя
# производилось логирование
user_repo = UserRepositoryWithLogging(user_repo)
# Выполняем поиск пользователя с помощью обёрнутого объекта репозитория
# и замечаем, что поведение дли клиента не изменилось, однако в консоль выводятся логи
print(find_user(user_repo, "Awesome"))
# Для уведомления администратора декоратор пишется аналогичным образом.
# Также декораторы можно вкладывать друг в друга для получения комбинированных эффектов.
#
# Более подробную и каноничную версию реализации шаблоны "Декоратор"
# с использование абстрактного класса вы можете найти в описании
# ролика.
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment