Created
February 13, 2024 19:24
-
-
Save DanyaIzm/1227f2f0949499d8e60a401f527d8acd to your computer and use it in GitHub Desktop.
Упрощённая реализация паттерна "Декоратор"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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