Skip to content

Latest commit

 

History

History
98 lines (70 loc) · 4.87 KB

File metadata and controls

98 lines (70 loc) · 4.87 KB

Паттерн Внедрение зависимостей

Внедрение зависимостей (Dependency injection, DI) - процесс предоставления внешней зависимости программному компоненту.
Специфичная форма IoC (Inversion of Control, инверсии потока управления),
при которой с абстракциями и наследованиями код пишется от общего к частному.

Благодаря IoC при изменении частного не нужно переделывать всю программу, когда изменил частное.
Нужно лишь изменить классы, находящиеся ниже по пирамиде иерархии абстракций, а не выше.
Таким образом, разработка идет не снизу вверх, а сверху вниз
(т.е. сначала мы проектируем/изменяем систему на уровне абстракций и интерфейсов, а потом вдаемся в детали и особенности технологии;
от общего -> к частному).

Существуют следующие 3 вида Dependency Injection:
через конструктор, метод и свойство
(а также через доп. библиотеки).

Service - класс, который выполняет какую-то задачу.
Service может быть неоднозначен.
Если Service поменяется через какое-то время, то у Client могут начаться проблемы, если он хранит Service напрямую.

Client - класс, которому нужен Service (другой модуль).

Injector - класс, который выбирает и внедряет (передает) сервис в клиент.
Injector является неким посредником для того,
чтобы уменьшить прямые зависимости и жесткую привязанность к конкретным реализациям.
Injector внедряет Сервисы в Клиенты через интерфейс.
Injector выбирает и распределяет по клиентам конкретные типы сервиса на основе каких-то настроек и конфигов.

Important

ВАЖНО: В результате клиент не знает с каким конкретно сервисом он работает в данный момент.
Таким образом, код испытывает меньше проблем с конфигурацией и становится более гибким и расширяемым.

Tip

Допустим, есть клиент, который хочет воспользоваться сначала сервисом отправки сообщения через почту, а потом через мессенджер.

Делегируем ответственность, чтобы клиент не занимался лишним - распределением между сервисами.

Клиент просто будет иметь функцию send_text, а также функцию inject, в которую будет вставлять сервис уже специально обученный для этого Injector.

from pydantic import BaseModel
from abc import ABC, abstractmethod


class Service(ABC):
    @abstractmethod
    def send(self, text: str):
        pass


class MailService(Service):
    def send(self, text: str):
        print(f'Отправляю через почту текст "{text}"')


class MessengerService(Service):
    def send(self, text: str):
        print(f'Отправляю через мессенджер текст "{text}"')


class Client(BaseModel):
    _service: Service

    def inject(self, service: Service):
        self._service = service

    def send_text(self, text: str):
        self._service.send(text)


class Injector(BaseModel):
    _client: Client

    def __init__(self, client: Client, server_type: str):
        _client = client
        service: Service
        match server_type:
            case "mail":
                service = MailService()
            case "messenger":
                service = MessengerService()
        _client.inject(service)


client = Client()

injector = Injector(client, "mail")
client.send_text("Привет!")

injector = Injector(client, "messenger")
client.send_text("Хэй!")

# output:
# Отправляю через почту текст "Привет!"
# Отправляю через мессенджер текст "Хэй!"