Каков принцип инверсии зависимости и почему это важно?

Указатель NULL - это тот, который указывает на никуда. Когда вы разыскиваете указатель p, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p является нулевым указателем, местоположение, хранящееся в p, является nowhere, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception.

В общем, это потому, что что-то не было правильно инициализировано.

170
задан Peter Mortensen 22 June 2017 в 07:35
поделиться

7 ответов

Проверьте этот документ: Принцип Инверсии Зависимости .

Это в основном говорит:

  • модули Высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций.
  • Абстракции никогда не должны зависеть от деталей. Детали должны зависеть от абстракций.

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

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

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

103
ответ дан Nick Weaver 23 November 2019 в 20:48
поделиться

Инверсия Контейнеров Управления и шаблона Внедрения зависимости Martin Fowler является хорошим чтением также. Я нашел Главные Первые Шаблоны разработки потрясающая книга для моего первого набега в изучение DI и других шаблонов.

0
ответ дан Chris Canal 23 November 2019 в 20:48
поделиться

Инверсия управления (МОК) является шаблоном разработки, куда объекту вручает его зависимость внешняя платформа, вместо того, чтобы просить у платформы его зависимость.

Псевдопример кода с помощью традиционного поиска:

class Service {
    Database database;
    init() {
        database = FrameworkSingleton.getService("database");
    }
}

Подобное использование кода МОК:

class Service {
    Database database;
    init(database) {
        this.database = database;
    }
}

преимущества МОК:

  • у Вас нет зависимости от центральной платформы, таким образом, это может быть изменено при желании.
  • , Так как объекты создаются инжекцией, предпочтительно с помощью интерфейсов, легко создать модульные тесты, которые заменяют зависимости ложными версиями.
  • Разъединение от кода.
4
ответ дан Peter Mortensen 23 November 2019 в 20:48
поделиться

Хорошие ответы и хорошие примеры уже даны другими здесь.

причина , DIP важен, состоит в том, потому что это гарантирует, что принцип OO "слабо связал дизайн".

объекты в Вашем программном обеспечении не должны входить в иерархию, где некоторые объекты являются верхнего уровня, зависящими от объектов низкого уровня. Изменения в объектах низкого уровня будут тогда слегка колебаться - до Ваших объектов верхнего уровня, который делает программное обеспечение очень хрупким для изменения.

Вы хотите свои объекты 'верхнего уровня' быть очень стабильными и не хрупкими для изменения, поэтому необходимо инвертировать зависимости.

5
ответ дан Peter Mortensen 23 November 2019 в 20:48
поделиться

Точка инверсии зависимости должна сделать допускающее повторное использование программное обеспечение.

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

способ, которым это обычно достигается, через контейнер инверсии управления (IoC) как Spring в Java. В этой модели свойства объектов настраиваются через конфигурацию XML вместо объектов, выходящих и находящих их зависимость.

Воображают этот псевдокод...

public class MyClass
{
  public Service myService = ServiceLocator.service;
}

MyClass непосредственно зависит и от Класса обслуживания и от класса ServiceLocator. Этому нужны оба из тех, если Вы хотите использовать его в другом приложении. Теперь вообразите это...

public class MyClass
{
  public IService myService;
}

Теперь, MyClass полагается на единственный интерфейс, интерфейс IService. Мы позволили контейнеру МОК на самом деле установить значение той переменной.

Поэтому теперь, MyClass может легко быть снова использован в других проектах, не принося зависимость тех других двух классов наряду с ним.

Еще лучше, Вы не должны перетаскивать зависимости MyService и зависимости тех зависимостей, и... хорошо, Вы получаете идею.

2
ответ дан Peter Mortensen 23 November 2019 в 20:48
поделиться

Лучшими являются книги «Гибкая разработка программного обеспечения, Принципы, шаблоны и практики» и «Гибкие принципы, шаблоны и практики на C #». ресурсы для полного понимания исходных целей и мотивов, лежащих в основе принципа инверсии зависимостей. Статья «Принцип инверсии зависимостей» также является хорошим ресурсом, но из-за того, что это сокращенная версия черновика, которая в конечном итоге попала в упомянутые ранее книги, в ней не учтены некоторые важные дискуссии о концепции владение пакетом и интерфейсом, которые являются ключом к отличию этого принципа от более общих рекомендаций «программировать интерфейс, а не реализации», содержащихся в книге «Шаблоны проектирования» (Gamma, et. al).

Подводя итог, принцип инверсии зависимостей в основном касается изменения обычного направления зависимостей от компонентов «более высокого уровня» к компонентам «более низкого уровня», так что компоненты «более низкого уровня» зависят от интерфейсы , принадлежащие компонентам «более высокого уровня». (Примечание: «компонент более высокого уровня» здесь относится к компоненту, требующему внешних зависимостей / сервисов, не обязательно его концептуальной позиции в многоуровневой архитектуре.) При этом связь не уменьшается так сильно, как она есть. перешел от компонентов, которые теоретически менее ценны, к компонентам, которые теоретически более ценны.

Это достигается путем разработки компонентов, внешние зависимости которых выражаются в терминах интерфейса, реализация которого должна быть предоставлена ​​потребителем компонента. Другими словами, определенные интерфейсы выражают то, что необходимо компоненту, а не то, как вы его используете (например, «INeedSomething», а не «IDoSomething»).

Принцип инверсии зависимостей не относится к простой практике абстрагирование зависимостей с помощью интерфейсов (например, MyService → [ILogger ⇐ Logger]). Хотя это отделяет компонент от конкретной детали реализации зависимости, это не меняет отношения между потребителем и зависимостью (например, [MyService → IMyServiceLogger] ⇐ Logger. (ve разработал компоненты бизнес-логики, которые остаются неизменными во многих версиях приложения, детали реализации которых развиваются).

В первом случае повторного использования компонентов в нескольких приложениях, например, с библиотекой инфраструктуры, цель состоит в том, чтобы предоставить вашим потребителям базовую инфраструктуру, не привязывая потребителей к подчиненным зависимостям вашей собственной библиотеки, так как они принимают зависимости от таких dependencies требует, чтобы ваши потребители также требовали таких же зависимостей. Это может быть проблематично, если потребители вашей библиотеки решат использовать другую библиотеку для тех же потребностей инфраструктуры (например, NLog или log4net), или если они решат использовать более позднюю версию требуемой библиотеки, которая не имеет обратной совместимости с этой версией. требуется вашей библиотеке.

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

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

Хотя следование принципу инверсии зависимостей в этом втором случае может дать некоторые преимущества, следует отметить, что его ценность применительно к современным языкам, таким как Java и C #, очень велика. уменьшено, возможно, до того, что стало неактуальным. Как обсуждалось ранее, DIP включает полное разделение деталей реализации на отдельные пакеты. Однако в случае развивающегося приложения простое использование интерфейсов, определенных в терминах бизнес-домена, защитит от необходимости изменять компоненты более высокого уровня из-за меняющихся потребностей компонентов деталей реализации, даже если детали реализации в конечном итоге находятся в одном пакете. . Эта часть принципа отражает аспекты, которые имели отношение к рассматриваемому языку, когда принцип был кодифицирован (например, C ++), которые не относятся к новым языкам. Тем не менее, важность принципа инверсии зависимостей в первую очередь связана с разработкой повторно используемых программных компонентов / библиотек.

Более подробное обсуждение этого принципа, поскольку он касается простого использования интерфейсов, внедрения зависимостей и шаблона разделенного интерфейса, может можно найти здесь . Кроме того, обсуждение того, как этот принцип относится к языкам с динамической типизацией, таким как JavaScript, можно найти здесь .

Важность принципа инверсии зависимостей в первую очередь заключается в разработке повторно используемых программных компонентов / библиотек.

Более подробное обсуждение этого принципа, поскольку он касается простого использования интерфейсов, внедрения зависимостей и шаблона раздельного интерфейса, можно найти здесь . Кроме того, обсуждение того, как этот принцип относится к языкам с динамической типизацией, таким как JavaScript, можно найти здесь .

Важность принципа инверсии зависимостей в первую очередь заключается в разработке повторно используемых программных компонентов / библиотек.

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

140
ответ дан 23 November 2019 в 20:48
поделиться

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

Проблема в C ++ заключается в том, что файлы заголовков обычно содержат объявления частных полей и методов. Следовательно, если высокоуровневый модуль C ++ включает файл заголовка для низкоуровневого модуля, это будет зависеть от фактических деталей реализации этого модуля. И это, очевидно, нехорошо. Но это не проблема для более современных языков, обычно используемых сегодня.

Модули высокого уровня по своей природе менее пригодны для повторного использования, чем модули низкого уровня, потому что первые обычно более специфичны для приложения / контекста, чем вторые. Например, компонент, реализующий экран пользовательского интерфейса, относится к высшему уровню и также очень (полностью?) специфичен для приложения. Попытка повторно использовать такой компонент в другом приложении контрпродуктивна и может привести только к чрезмерной инженерии.

Итак, создание отдельной абстракции на том же уровне компонента A, которая зависит от компонента B ( которое не зависит от A), может быть выполнено только в том случае, если компонент A действительно будет полезен для повторного использования в различных приложениях или контекстах. Если это не так, то применение DIP было бы плохим дизайном.

создание отдельной абстракции на том же уровне компонента A, которая зависит от компонента B (который не зависит от A), может быть выполнено только в том случае, если компонент A действительно будет полезен для повторного использования в различных приложениях или контекстах. Если это не так, то применение DIP было бы плохим дизайном.

создание отдельной абстракции на том же уровне компонента A, которая зависит от компонента B (который не зависит от A), может быть выполнено только в том случае, если компонент A действительно будет полезен для повторного использования в различных приложениях или контекстах. Если это не так, то применение DIP было бы плохим дизайном.

10
ответ дан 23 November 2019 в 20:48
поделиться