Слабосвязанный код Python с внедрением зависимостей

Программное обеспечение должно быть гибким, чтобы реагировать на изменения. Внедрение зависимостей — это метод управления зависимостями между программными компонентами. Это шаблон проектирования, который использует инверсия контроля чтобы обеспечить возможность замены компонентов для тестирования или других целей без изменения какой-либо другой части системы.

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

Основная идея внедрения зависимостей состоит в том, чтобы передать ответственность за предоставление услуг от функций-конструкторов или прямых методов создания экземпляров другим объектам. Другими словами, объект получает свои зависимости, а не создает их. Таким образом, если мы хотим добавить новые функции, нам не нужно изменять существующий код, а вместо этого передавать в него новые зависимости.


Создание абстракций с интерфейсами

Написание слабосвязанного кода Python с внедрением зависимостей состоит из трех шагов. Первый шаг — определить, какие зависимости нужны коду, второй шаг — создать интерфейсы для каждой зависимости, а третий шаг — передать их зависимому объекту через его конструктор или параметры метода.

Недавно я реализовал функциональность, которая требовала байтового кодировщика и декодера для манипулирования бизнес-данными (Я упростил его реализацию для удобства чтения). Я перенес их обязанности на интерфейсы. В Python нет ключевого слова interface, как в Golang, но вы можете воспроизвести функциональность интерфейса с помощью азбука библиотека 😎.

Интерфейсы для кодера и декодера.

Ориентируясь на интерфейс IPacketEncoder, конкретный класс кодировщика должен его реализовать. Первоначальные бизнес-требования требовали, чтобы все типы были закодированы, а строки были дополнены их длиной. К счастью, Python предоставляет структура библиотека для выполнения большей части тяжелой работы.

Конкретный класс PaddedPacketEncoder.


Реализация внедрения зависимостей

Функция encode_name — это простая реализация для создания закодированной полезной нагрузки с указанным именем. Он принимает объект IPacketEncoder и строку в качестве параметров.

Экземпляр PaddedPacketEncoder передается в функцию encode_name.

Две ключевые детали реализации — это использование интерфейса IPacketEncoder вместо конкретного класса PaddedPacketEncoder, а encoder — это аргумент, а не инициализация внутри функции. Это внедрение зависимостей. Поскольку IPacketEncoder передается, его можно легко расширить, изменить или снабдить заглушкатем самым оставляя систему слабо связанной.

Несколько недель спустя появилось новое требование к кодировщику для обработки кодировок с завершающим нулем. К счастью, благодаря существующей реализации его можно легко реализовать.

Конкретный класс NullTerminatedPacketEncoder.

NullTerminatedPacketEncoder по существу добавляет NULL_BYTE после каждой закодированной строки. Возвращаясь к функции encode_name, экземпляр NullTerminatedPacketEncoder является допустимым аргументом кодировщика, поскольку NullTerminatedPacketEncoder реализует интерфейс IPacketEncoder аналогично PaddedPacketEncoder.

NullTerminatedPacketEncoder передается в функцию encode_name.

Если бы здесь не использовалась инъекция зависимостей, это могло бы привести к сильно связанному коду, который трудно изменить, например:

Парное кодирование кода Python.

Если потребуется новый кодировщик, функцию encode_name придется почти полностью изменить вместе с ее тестами, что продлит время доставки. Что еще хуже, это может привести к скрытым побочным эффектам, особенно если исходный разработчик больше недоступен.

В заключение я приведу менее упрощенный пример использования этой техники. SocketService отвечает за декодирование кода операции, вызов соответствующего обработчика и возврат результата. Все его типы аргументов являются абстракциями интерфейса, включая его конструктор. CLI отвечает за определение того, какой кодировщик использовать, т. е. NullTerminated или Padded, и передачу его экземпляра классу SocketService.

SocketService, использующий внедрение зависимостей.

Поскольку проекты продолжают расти, рекомендуется использовать структуру внедрения зависимостей для «внедрения» этих зависимостей, таких как Инжектор зависимостейчтобы автоматически вводить аргументы зависимости ✨.

Надеюсь, это демонстрирует, как писать слабосвязанный код Python с внедрением зависимостей. Многим разработчикам Python не нравится внедрение зависимостей, потому что они считают его не «питоновским», но я бы сказал, что его преимущества не могут быть дискредитированы, особенно когда он присутствует во многих других языках программирования.

Спасибо за прочтение 😊. Если вы хотите связаться со мной, вы можете найти меня на Линкедин или же Гитхаб.