Что означает "абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций" в смысле принципа инверсии зависимости [DIP]?
Прежде чем задать этот вопрос, я хотел бы сказать, что этот вопрос в stackru очень похож на мой вопрос, но, тем не менее, концепция не совсем понятна и очень запутана.
Я пытаюсь понять принцип инверсии зависимости, но не могу понять его полностью?
Ниже приведены два пункта, о которых говорит DIP
A. Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций. Б. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Я могу понять первую точку, но не могу получить вторую точку, она выглядит так, как будто они одинаковы. После большого количества поисков в stackru и других сайтах я могу понять, что оба пытаются сказать что-то другое, но я не смог получить его.
Давайте рассмотрим пример:
Рассмотрим класс SalaryCalculator [модуль высокого уровня], который используется для расчета зарплаты сотрудника. Который использует BonusCalculator [модуль высокого уровня] для расчета зарплаты, как показано ниже. Поскольку SalaryCalculator использует BonusCalculator, он нарушает первый пункт "Модули высокого уровня не должны зависеть от модулей низкого уровня. И то, и другое должно зависеть от абстракций ".
Итак, мы ввели абстракцию между ними, как показано ниже:
Здесь детали [Модули низкого и высокого уровня] зависят от абстракции, а абстракция не зависит от деталей. Так в DIP, что пытается сказать вторая точка? Если оба одинаковы, почему это делается в виде двух пунктов?
Если кто-нибудь даст мне пример кода, это будет очень полезно.
2 ответа
Давайте разберем эту часть B дальше.
Абстракции не должны зависеть от деталей. Это может означать, что объявление вашего интерфейса (ваша абстракция) должно избегать включения конкретных типов. Подумайте о разнице между distance(int X1, int Y1, int X2, int Y2)
а также distance(Point A, Point B)
, Что если у вас есть координаты, измеренные в плавающей точке, широте или долготе или в полярных системах координат? Что если вы перейдете в 3D-пространство? Вам придется переопределять каждую процедуру, которая использует вашу функцию расстояния.
Детали должны зависеть от абстракций. Насколько это возможно, продолжайте использовать уровень абстракции, чтобы избежать зависимостей от конкретных типов.
Это все о минимизации воздействия изменений. Чем меньше ваш код зависит от других вещей, тем больше он позволяет изменить этот другой код.
Этот вопрос действительно лежит в основе того, почему ООП полезен и почему абстракции так важны для информатики. По сути, мы используем абстракции, когда хотим скрыть сложность (детали) от пользователей нашего программного обеспечения.
Например, если я пишу Калькулятор бонусов, а вы пишете Калькулятор заработной платы, я хочу иметь возможность вносить изменения в свой код, не нарушая ваше приложение.
Это требует, чтобы мы оба договорились о конкретной абстракции, которая никогда не изменится. Я предоставляю вам методы для доступа к функциональности моего кода и обещаю, что вызов этих методов всегда даст одинаковые результаты, даже если "детали" моей реализации могут изменяться со временем.
Итак, возвращаясь к исходным вопросам:
A. Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций.
- "Абстрагируясь" от функциональности, содержащейся в модуле низкого уровня (Bonus Calculator), вы сможете относительно легко переключиться на чужой бонусный калькулятор, если сочтете мой сервис ненужным.
- Это потому, что вы защитили себя от "деталей" моего кода с помощью абстракции.
Б. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
- Если бы ваша абстракция зависела от деталей моего кода, то вам пришлось бы все переписать, чтобы перейти на новый бонусный калькулятор! Это победит цель.
Пример кода (JavaScript):
- Скажем, у нас есть абстракция под названием "сумма", которая просто вычисляет сумму двух чисел. Вы являетесь пользователем этой функции и хотите использовать ее следующим образом: sum(2,2) = 4.
Теперь скажем, что есть два разных модуля (функции), которые вычисляют сумму.
function sum(a, b) { return a + b }
function sum(b, a) { return b + a }
Очевидно, что эти функции абсолютно одинаковы, но представьте, если бы это было сложное вычисление с множеством различных способов достижения результата, и у каждой была совершенно разная производительность во время выполнения. Вы можете свободно проверить, какая функция работает лучше для вас, используя тот же интерфейс: просто вызывая sum(). Абстракция не зависит от деталей.
- Таким образом, ваш модуль высокого уровня также больше не зависит от модуля низкого уровня, поскольку вы можете попробовать другой модуль низкого уровня с относительной легкостью.
Извините, этот ответ немного грязный. Надеюсь это поможет!