обзор
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 здесь .