Статьи

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


обзор

Dependency Injection (DI) — это метод разработки программного обеспечения для определения зависимостей между объектами. По сути, это процесс предоставления ресурса, который требует данный фрагмент кода. Требуемый ресурс называется зависимостью.

Существуют различные классы и объекты, определенные при написании кода. В большинстве случаев эти классы зависят от других классов, чтобы выполнить свое предназначение. Эти классы, или, лучше сказать, «Компоненты», знают, какие ресурсы им нужны, и как их получить. DI управляет определением этих зависимых ресурсов и предоставляет способы их создания или внешнего создания. Контейнеры зависимостей используются для реализации этого поведения и содержат карту зависимостей для компонентов.

Если объект A зависит от объекта B, объект A не должен создавать или импортировать объект B напрямую. Вместо этого, объект А должен обеспечить способ для введения объекта B . Обязанности создания объекта и внедрения зависимости делегируются внешнему коду.

Зачем использовать инъекцию зависимостей в вашем коде?

  • Гибкость конфигурируемых компонентов: поскольку компоненты конфигурируются извне, для компонента могут быть различные определения (Контроль над структурой приложения).
  • Тестирование стало проще: создание фиктивных объектов и интеграция с определениями классов проще.
  • Высокая сплоченность: код с уменьшенной сложностью модуля, повышенной возможностью повторного использования модуля.
  • Минималистичные зависимости: поскольку зависимости четко определены, проще устранить / уменьшить ненужные зависимости.

Реализация DI в Python

Внедрение зависимостей в Python мало отличается от общего статического языка DI. Python имеет библиотеку микрорамок для DI, которая называется dependency_injector . Этот пакет имеет две основные сущности: контейнеры и провайдеры.

Поставщики описывают, как осуществляется доступ к объектам. Контейнеры — это просто набор провайдеров. Наиболее часто используемые типы провайдеров: Singleton, Configuration и Factory.

пример

В следующем примере демонстрируется использование и реализация DI в Python. Создайте файл с именем email_client.py, содержащий  EmailClient класс, который зависит от объекта.

class EmailClient(object):

    def __init__(self, config):
        self._config = config
        self.connect(self._config)

    def connect(self, config):
        # Implement function here
        pass

Создайте новый файл с именем e mail_reader.py, который содержит   EmailReader класс и зависит от  EmailClient объекта.

class EmailReader(object):

    def __init__(self, client):
        try:
            self._client = client
        except Exception as e:
            raise e

    def read(self):
        # Implement function here
        pass

Теперь, чтобы определить вышеуказанные зависимости извне, создайте новый файл container.py . Импортируйте пакет dependency_injector и классы, которые будут использоваться в DI.

from dependency_injector import providers, containers
from email_client import EmailClient
from email_reader import EmailReader

Добавьте класс  Configs в файл. Этот класс является контейнером с поставщиком конфигурации, который предоставляет все объекты конфигурации.

class Configs(containers.DeclarativeContainer): 
  config = providers.Configuration('config') 
  # other configs

Добавить еще один класс  Clients. Этот класс является контейнером, определяющим все виды клиентов. EmailClient создается с одноэлементным провайдером, утверждая отдельные экземпляры этого класса и определяя его зависимость от объекта.

class Clients(containers.DeclarativeContainer): 
  email_client = providers.Singleton(EmailClient, Configs.config) 
  # other clients 

Третий контейнер — это  Readersкласс, определяющий зависимость  EmailReader класса от  EmailClient класса.

class Readers(containers.DeclarativeContainer): 
  email_reader = providers.Factory(EmailReader, client=Clients.email_client) 
  # other readers 
from dependency_injector import providers, containers
from email_client import EmailClient
from email_reader import EmailReader

class Configs(containers.DeclarativeContainer):
    config = providers.Configuration('config')
    # other configs

class Clients(containers.DeclarativeContainer):
    email_client = providers.Singleton(EmailClient, Configs.config)
    # other clients

class Readers(containers.DeclarativeContainer):
    email_reader = providers.Factory(EmailReader, client=Clients.email_client)
    # other readers

Для запуска примера создайте  файл main.py со следующим кодом.

from containers import Readers, Clients, Configs

if __name__ == "__main__":
    Configs.config.override({
        "domain_name": "imap.gmail.com",
        "email_address": "YOUR_EMAIL_ADDRESS",
        "password": "YOUR_PASSWORD",
        "mailbox": "INBOX"
    })
    email_reader = Readers.email_reader()
    print email_reader.read()

В файле main.py объект переопределяется данным объектом словаря. EmailReader Класс был экземпляр без экземпляра   EmailClient класса в  главном файле, удаление накладных расходов импорта или создания. Об этой части заботится файл контейнеров .

Для рабочего примера, пожалуйста, обратитесь к моему репозиторию GitHub  здесь .