В настоящее время я пытаюсь выяснить, ТВЕРДЫЙ. Таким образом, принцип обращения зависимостей означает, что любые два класса должны взаимодействовать через интерфейсы, а не напрямую. Пример: если class A
есть метод, который ожидает указатель на объект типа class B
, то этот метод должен фактически ожидать объект типа abstract base class of B
. Это также помогает для открытия / закрытия.
При условии, что я правильно понял, мой вопрос будет состоять в том, является ли хорошей практикой применение этого ко всем взаимодействиям классов или я должен попытаться мыслить с точки зрения слоев ?
Причина, по которой я скептически отношусь, заключается в том, что мы платим некоторую цену за следование этому принципу. Скажем, мне нужно реализовать функцию Z
. После анализа я пришел к выводу, что функция Z
состоит из функциональности A
, B
и C
. Я создать фасад класс Z
, который, через интерфейсы, использует классы A
, B
и C
. Я начинаю кодировать реализацию и в какой-то момент понимаю, что задача на Z
самом деле состоит из функциональности A
, B
и D
. Теперь мне нужно пересмотреть C
интерфейс, C
прототип класса и написать отдельный D
интерфейс и класс. Без интерфейсов только класс должен был бы быть заменен.
Другими словами, чтобы что-то изменить, мне нужно поменять 1. вызывающую сторону 2. интерфейс 3. декларацию 4. реализацию. В реализации, напрямую связанной с Python, мне нужно будет изменить только реализацию.
Ответы:
Во многих мультфильмах и других средствах массовой информации силы добра и зла часто изображаются ангелом и демоном, сидящим на плечах персонажа. В нашей истории здесь, вместо добра и зла, мы имеем ТВЕРДЫЕ на одном плече, а ЯГНИ (Тебе это не понадобится!) На другом.
Максимальные принципы SOLID лучше всего подходят для огромных, сложных, ультраконфигурируемых корпоративных систем. Для небольших или более специфичных систем неуместно делать все смехотворно гибким, так как время, которое вы тратите на абстрагирование, не принесет пользы.
Передача интерфейсов вместо конкретных классов иногда означает, например, что вы можете легко поменять чтение из файла для сетевого потока. Однако для большого количества программных проектов, что такая гибкость просто не когда - либо будет необходимо, и вы можете также просто передать конкретные классы файлов и назвать это день и избавить клетки мозга.
Часть искусства разработки программного обеспечения имеет хорошее представление о том, что может измениться со временем, а что нет. Для вещей, которые могут измениться, используйте интерфейсы и другие концепции SOLID. Для вещей, которые не будут, используйте YAGNI и просто передавайте конкретные типы, забудьте фабричные классы, забудьте все подключения и настройки во время выполнения и т. Д., И забудьте много абстракций SOLID. По моему опыту, подход YAGNI оказался верным гораздо чаще, чем нет.
источник
File
, так что вместо этого потребуетсяIFile
, работа сделана». Тогда они не могут легко заменить сетевой поток, потому что они чрезмерно требовали интерфейс, и вIFile
методе даже не используются операции , которые не применяются к сокетам, поэтому сокет не может быть реализованIFile
. Одной из вещей, для которой DI не является серебряной пулей, является изобретение правильных абстракций (интерфейсов) :-)По словам непрофессионала:
Применять DIP легко и весело . Неправильный дизайн с первой попытки - не достаточная причина для отказа от DIP.
С другой стороны, программирование с интерфейсами и OOD может вернуть радость к иногда устаревшему ремеслу программирования.
Некоторые люди говорят, что это добавляет сложности, но я думаю, что опоссит это правда. Даже для небольших проектов. Это делает тестирование / издевательство легче. Это делает ваш код иметь меньше, если какие-либо
case
операторы или вложенныеifs
. Это уменьшает цикломатическую сложность и заставляет вас думать по-новому. Это делает программирование более похожим на реальный дизайн и производство.источник
Используйте инверсию зависимостей там, где это имеет смысл.
Одним из крайних контрпримеров является класс «string», включенный во многие языки. Он представляет собой примитивную концепцию, по сути, массив символов. Предполагая, что вы можете изменить этот базовый класс, нет смысла использовать DI здесь, потому что вам никогда не придется менять внутреннее состояние на что-то другое.
Если у вас есть группа объектов, внутренне используемых в модуле, которые не представлены другим модулям или нигде не используются повторно, вероятно, не стоит усилий по использованию DI.
Есть два места, где DI должен автоматически использоваться по моему мнению:
В модулях, предназначенных для расширения. Если целью модуля является расширение его и изменение поведения, то имеет смысл запрограммировать DI с самого начала.
В модулях, которые вы реорганизуете с целью повторного использования кода. Возможно, вы закодировали класс, чтобы что-то сделать, а затем поняли, что с помощью рефакторинга вы можете использовать этот код в другом месте, и это необходимо сделать . Это отличный кандидат для DI и других изменений расширяемости.
Ключи здесь используют его там, где это необходимо, потому что это создаст дополнительную сложность, и убедитесь, что вы измеряете эти потребности либо с помощью технических требований (точка первая), либо количественного анализа кода (точка вторая).
DI - отличный инструмент, но, как и любой инструмент *, он может быть использован чрезмерно или неправильно.
* Исключение из вышеуказанного правила: поршневая пила - идеальный инструмент для любой работы. Если это не решит вашу проблему, он удалит ее. Постоянно.
источник
String
типа, не расширяемого пользователем , во многих случаях было бы полезно использовать альтернативные представления, если бы у типа был хороший набор виртуальных операций (например, скопировать подстроку в указанную частьshort[]
, сообщить, подстрока содержит или может содержать только ASCII, попытаться скопировать подстроку, которая, как считается, содержит только ASCII, в указанную часть abyte[]
и т. д.) Это очень плохо, фреймворки не имеют своих строковых типов, реализующих какие-либо полезные интерфейсы, связанные со строками.Мне кажется, что в первоначальном вопросе отсутствует часть сути DIP.
Чтобы по-настоящему воспользоваться DIP, вы сначала должны создать класс Z, и он будет вызывать функциональность классов A, B и C (которые еще не разработаны). Это дает вам API для классов A, B и C. Затем вы создаете классы A, B и C и заполняете детали. По сути, вы должны создавать необходимые абстракции при создании класса Z, основываясь исключительно на том, что нужно классу Z. Вы даже можете написать тесты вокруг класса Z, прежде чем классы A, B или C даже будут написаны.
Помните, что DIP говорит, что «модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций».
После того, как вы выяснили, что нужно классу Z и каким образом он хочет получить то, что ему нужно, вы можете заполнить детали. Конечно, иногда нужно будет внести изменения в класс Z, но в 99% случаев этого не произойдет.
Класса D никогда не будет, потому что вы решили, что Z нужны A, B и C до того, как они были написаны. Изменение требований - это совсем другая история.
источник
Краткий ответ «почти никогда», но есть несколько мест, где DIP не имеет смысла:
Заводы или строители, чья работа заключается в создании объектов. По сути, это «конечные узлы» в системе, которая полностью охватывает IoC. В какой-то момент что-то действительно должно создавать ваши объекты, и не может зависеть от чего-либо еще, чтобы это сделать. На многих языках контейнер IoC может сделать это за вас, но иногда вам нужно делать это старомодным способом.
Реализации структур данных и алгоритмов. Как правило, в этих случаях характерные характеристики, для которых вы оптимизируете (например, время выполнения и асимптотическая сложность), зависят от используемых типов данных. Если вы реализуете хеш-таблицу, вам действительно нужно знать, что вы работаете с массивом для хранения, а не со связанным списком, и только сама таблица знает, как правильно распределить массивы. Вы также не хотите передавать изменяемый массив и вызывать вызывающую функцию для разбивания вашей хеш-таблицы, манипулируя ее содержимым.
Классы предметной модели . Они реализуют вашу бизнес-логику, и (большую часть времени) имеет смысл иметь только одну реализацию, потому что (большую часть времени) вы разрабатываете программное обеспечение только для одного бизнеса. Хотя некоторые классы модели предметной области могут быть построены с использованием других классов модели предметной области, обычно это происходит на индивидуальной основе. Поскольку объекты модели предметной области не включают в себя никакой функциональности, которая может быть с пользой смоделирована, для DIP нет преимуществ в тестируемости или удобстве обслуживания
Любые объекты, которые предоставляются в качестве внешнего API и должны создавать другие объекты, детали реализации которых вы не хотите раскрывать публично. Это подпадает под общую категорию «дизайн библиотеки отличается от дизайна приложения». Библиотека или фреймворк могут свободно использовать DI внутри, но в конечном итоге придется выполнять какую-то реальную работу, иначе это не очень полезная библиотека. Допустим, вы разрабатываете сетевую библиотеку; Вы действительно не хотите, чтобы потребитель мог предоставить собственную реализацию сокета. Вы можете внутренне использовать абстракцию сокета, но API, который вы предоставляете вызывающим, собирается создавать свои собственные сокеты.
Модульные тесты и двойные тесты Подделки и окурки должны делать одно и просто. Если у вас есть фальшивка, достаточно сложная, чтобы беспокоиться о том, делать инъекцию зависимостей или нет, тогда она, вероятно, слишком сложна (возможно, потому, что она реализует интерфейс, который также слишком сложен).
Там может быть больше; это те, которые я вижу довольно часто.
источник
Некоторые признаки того, что вы можете применять DIP на слишком микро уровне, где это не дает значения:
Если это то, что вы видите, возможно, было бы лучше, если бы Z напрямую вызвал C и пропустил интерфейс.
Кроме того, я не думаю, что оформление методов с помощью внедрения зависимостей / динамического прокси-сервера (Spring, Java EE) аналогично истинному SOLID DIP - это больше похоже на детали реализации того, как оформление методов работает в этом техническом стеке. Сообщество Java EE считает улучшением то, что вам не нужны пары Foo / FooImpl, как вы привыкли ( ссылка ). В отличие от этого, Python поддерживает оформление функций как первоклассную языковую функцию.
Смотрите также этот пост в блоге .
источник
Если вы всегда инвертируете свои зависимости, то все ваши зависимости перевернуты. Это означает, что если вы начали с грязного кода с узлом зависимостей, это то, что у вас все еще (на самом деле) есть, просто инвертированный. В этом и заключается проблема, заключающаяся в том, что каждое изменение в реализации также должно менять свой интерфейс.
Точка инверсии зависимостей заключается в том, что вы выборочно инвертируете зависимости, которые запутывают вещи. Те, кто должен идти от А до Б до С, все еще делают это, а те, которые шли от С до А, теперь идут от А до С.
Результатом должен быть граф зависимостей, свободный от циклов - DAG. Существуют различные инструменты , которые проверяют это свойство и рисуют график.
Для более полного объяснения см. Эту статью :
источник