Заместитель

описание
Заместитель — структурный шаблон проектирования, который предоставляет объект, который контролирует доступ к другому объекту, перехватывая все вызовы (выполняет функцию контейнера). Замещающийся объект может быть дорогостоящим в создании или требовать дополнительную функциональность, поэтому создается замещающий объект.


Используя шаблон заместитель, класс отображает функциональность другого класса.

Защитный заместитель
Класс который контролирует доступ до определенной функциональности. Рассмотрим пример создания прокси-класса (заместителя) для класса Bank, который расширяет функционал по ограничению доступа клиентам по возрасту.

При расширении функциональности создается новый класс BankProxy без модификации класса Bank

from dataclasses import dataclass
from typing import ClassVar


@dataclass
class User:
    name: str
    age: int


@dataclass
class Bank:
    client: User

    def payment(self) -> None:
        print(f'Предоставлен доступ для платежа {self.client.name}')


@dataclass
class BankProxy:
    user: User
    client: ClassVar

    def __post_init__(self):
        self.client = Bank(self.user)

    def payment(self) -> None:
        if self.user.age >= 18:
            self.client.payment()
        else:
            print('Доступ ограничен')


if __name__ == '__main__':
    client1 = BankProxy(User('Иван', 20))
    client1.payment()

    client2 = BankProxy(User('Матвей', 17))
    client2.payment()

Виртуальный заместитель
Класс, который маскирует нижележащую функциональность объекта, как будто объект инициализирован.

Рассмотрим пример создания класса заместителя загрузки веб-страницы SiteProxy. Перепишем метод get_page(), чтобы он проверял наличие страницы в кэше и не производил загрузку ее из интернета.

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import ClassVar


@dataclass
class ISite(ABC):

    @abstractmethod
    def get_page(self, page: int) -> str:
        pass


@dataclass
class Site(ISite):
    def get_page(self, page: int) -> str:
        return f'Загрузка страницы {page}'


@dataclass
class SiteProxy:
    __site: ISite
    __cache: ClassVar = {}

    def get_page(self, page: int) -> str:
        text: str = ''
        if self.__cache.get(page) is not None:
            text = self.__cache[page]
            return text + 'из кеша'
        text = self.__site.get_page(page)
        self.__cache[page] = text
        return text


if __name__ == '__main__':
    site1: ISite = SiteProxy(Site())

    print(site1.get_page(1))
    print(site1.get_page(2))
    print(site1.get_page(2))
    print(site1.get_page(1))