Оба модуля при добавлении абстракции/контракта/интерфейса в МВУ, после которого МВУ не зависит от МНУ, будут зависеть от абстракций по определению. МВУ будет зависеть от собственного интерфейса, а МНУ от интерфейса МВУ.
Проблема этого шага в том, что в реальной сложной системе это невыполнимо, поскольку не позволяет переиспользовать реализации с разными потребителями.
Ничего не мешает переиспользовать реализации с разными потребителями. В этом случае нужно использовать паттерн адаптер, о чем уже неоднократно сказали в комментариях.
Открываем википедию и видим ответ:
Как сказано выше, ничего вам не мешает переиспользовать реализации с разными потребителями, поэтомунепонятно на что вы нашли ответ.
В текущей статье сформулировал более доступные определения и то, почему они полностью соответствует определению, приведенному Мартином, объяснил в комментарии https://habr.com/ru/articles/872078/comments/#comment_27767816 Книгу Роберта Мартина не так просто читать, и для того, чтобы понять, что именно имеет ввиду автор, порой, нужно по 10 раз перечитывать каждую строку. Отчасти поэтому ведется столько споров вокруг принципов SOLID.
Аналогии нужны для того, чтобы доступнее объяснять сухие академические термины. Как видно по комментариям к данной статье, даже ссылаясь на эти академические термины, смысл их многим не понятен.
A strict definition of “level” is “the distance from the inputs and outputs.” The farther a policy is from both the inputs and the outputs of the system, the higher its level. The policies that manage input and output are the lowest-level policies in the system
Это формулировка совершенно не противоречит тому, что сказано в текущей статье про определение МВУ/МНУ, а наоборот, подтверждает корректность определений.
Следите за руками:
A strict definition of “level” is “the distance from the inputs and outputs.” The farther a policy is from both the inputs and the outputs of the system, the higher its level.
Переводим:
Строгое определение "уровня" — это "расстояние от ввода и вывода". Чем дальше политика от ввода и вывода, тем выше ее уровень.
Будем рассматривать "расстояние" только от ввода, т.к. для вывода все аналогично. Представим ввод в виде функции input(). Функция input() является крайней в стеке вызовов, или выражаясь аналогией из статьи, является последней в “пищевой цепочке”.
Теперь, давайте введем две функции: потребитель() и потребляемый(). Из функции потребитель() вызывается функция потребляемый(). Т.е. потребитель() использует/вызывает/потребляет реализацию функции потребляемый().
Мы должны определить исходя из вышеупомянутого определения Мартина, какая из функций является высокоуровневой, а какая низкоуровневой.
Посмотрим на стек вызовов:
Исходя из определения Мартина, высокоуровневая функция та, до которой расстояние больше. Из изображения выше видно и очевидно, что расстояние от input() до потребитель() всегда будет больше, чем расстояние от input() до потребляемый() и неважно сколько между потребитель()-потребляемый() и потребляемый()-input() будет промежуточных вызовов/абстракций. Поэтому в нашем случае, потребитель() — высокоуровневая функция, а потребляемый() — низкоуровневая.
Ну и уже, надеюсь, очевидное из сказанного/показанного выше, которое просто переведем:
The policies that manage input and output are the lowest-level policies in the system.
Переводим:
Политики, управляющие вводом и выводом, являются самыми низкоуровневыми в системе.
Возвращаясь к определениям из текущей статьи и исходя из вышесказанного можно смело утверждать, что расстояние до МВУ (до потребителя реализаций другого модуля) будет всегда больше, чем до МНУ, а значит определение из текущей статьи всегда будет соответствовать определению, приведенную Мартином.
То есть, у вас получается, что либа для общения по сети -- это модуль высокого уровня. У вас проблема с терминологией здесь -- почти все программисты скажут, что либа для общения по сети -- это низкоуровневая либа.
Сама по себе либа — это МВУ, т.к. зависит сама от себя и ей безразлично на код, который она будет использовать.
Уровни модулей определяются тем, кто чью реализацию вызывает/использует/потребляет, а не тем, чем код является (либой или фреймворком)
Я бы в это поверил, если бы дальше в этом же определении не было: модуль верхнего уровня — это модуль, который находится выше в «пищевой цепочке», т.е. потребляет (имеет внутри себя) код другого модуля.
Речь шла об аналогии:
Можно провести аналогию из природы: модуль верхнего уровня — это модуль, который находится выше в «пищевой цепочке», т.е. потребляет (имеет внутри себя) код другого модуля.
и здесь соглашусь, аналогию можно понять двояко. Но «костяк» описания более точный:
Модуль верхнего уровня (далее МВУ) — это модуль, который в конечном итоге использует реализацию другого модуля.
Поскольку нигде в ваших примерах напрямую код не включается, речь, очевидно, идет про ссылку (т.е. один модуль ссылается на элемент другого модуля).
Это совсем не очевидно и по комментариям к статье видно, что многие поняли о чем речь. Если кто-то не так понял термины, то значит формулировка в статье не идеальна.
В вашей финальной реализации ваш "МНУ" неизбежно ссылается на "МВУ" (потому что в нем определен интерфейс), значит, в вашей терминологии он стал модулем верхнего уровня.
Вы снова говорите про зависимость, которая, да, инвертировалась, как и должна была. Об этом более детально уже писал в комментарии выше.
Я вам честно скажу, не надо пытаться выкрутиться из этой ситуации, разруливая понятия "зависит" и "использует" - выход из нее состоит в том, чтобы не привязывать "верхний" и "нижний" уровни к использованию вообще (потому что нигде у Мартина такой привязки я не видел).
А к чему вы привязываете "верхний" и "нижний" уровни? Какая у вас формулировка термина?
кстати по логике вещей он должен располагаться вне обоих модулей, вне использующего и вне реализующего
Тогда каким образом зависимость инвертируется (поменяет свое направление на противоположное), какая конкретно это будет зависимость (которая ближе к МВУ или к МНУ) и почему именно она?
НЕТ!! Есть например модуль шифрования/обращения по сети/любая другая либа, с какого перепугу этот модуль должен реализовывать какие-то левые интерфейсы из приложений, если его использует 100500 приложений?
Очевидно, что библиотеки не должны реализовывать какие-то левые интерфейсы . Библиотека сама по себе является МВУ , т.е. самодостаточна и не зависят от клиентского кода. И если вы не хотите, чтобы в вашем клиентском МВУ (который не библиотека) использовалась/вызывалась какая-то библиотека, то вызов этой библиотеки нужно сделать в МНУ (другими словами, строить обертку над библиотекой). Т.е. если вы называете подключаемую стороннюю библиотеку модулем, то у вас в итоге 3 модуля: библиотека (МВУ), клиентский МВУ и МНУ, где вы «изолируете» библиотеку. Но это уже частное решение какой-то задачи с использованием DIP, а не DIP
Корректнее будет DIP — dependency inversion principle
С автором статьи и его пониманием модуля как папки и вводом границ не согласен. На мой взгляд это только вносит ещё большую путаницу.
Аналогию с папкой привел для простоты (как и сказано в статье) понимания. Если для кого-то это создает путаницу, то, вы правы, и, вероятно, есть более доходчивые формулировки. МВУ и МНУ могут быть и классами и лежать в одной директории, и даже находиться в одном файле
На схеме отмеченной галочкой видимо изображен верный вариант. Но разве в этом случае не зависит ли класс B от интерфейса, а следовательно и от модуля верхнего уровня, от которого зависеть не должен?
Почему B (МНУ) не должен зависеть от A (МВУ)? Про это в DIP не говорится. Мы инвертировали зависимость, и если раньше A зависел от B, то теперь B зависит от A.
Оба типа модулей отдельно, а абстракции отдельно.
Хорошее замечание. Но тогда, где будет инверсия зависимости? Т.е., в этом случае название принципа, как-будто бы не имеет отношения к его содержанию.
однако, меня сильно смущает, что теперь модуль, в котором находится В не может существовать без модуля, где поселился А и интерфейс, по той простой причине, что В должен реализовывать этот самый интерфейс ?!
Это и называется инверсией зависимости. Т.е. зависимость поменяла свое направление. Раньше A зависел от B, а после применения принципа B зависит от A. Кто-то от кого-то в любом случае будет зависеть
Но если в "МВУ" нет "ни одной строчки кода из МНУ", то... он его не использует.
Речь шла про зависимости. Если в модуле нет ни одной строчки кода, это означает, что он «не зависит», а не «не использует».
При этом поскольку ваш "МНУ" использует интерфейс из "МВУ", он... начал его использовать. У вас модули поменялись местами.
Здесь аналогично, речь про зависимости, а не «использование реализации»
Как сказано в статье:
Модуль верхнего уровня (далее МВУ) — это модуль, который в конечном итоге использует реализацию другого модуля
Обратите здесь особое внимание на слово «реализацию». Т.е., потребитель реализации как был МВУ, так им и остался. Поменялись местами не модули, а зависимости, т.е., другими словами, зависимость инвертировалась, что нам и было нужно.
Оба модуля при добавлении абстракции/контракта/интерфейса в МВУ, после которого МВУ не зависит от МНУ, будут зависеть от абстракций по определению. МВУ будет зависеть от собственного интерфейса, а МНУ от интерфейса МВУ.
Ничего не мешает переиспользовать реализации с разными потребителями. В этом случае нужно использовать паттерн адаптер, о чем уже неоднократно сказали в комментариях.
Как сказано выше, ничего вам не мешает переиспользовать реализации с разными потребителями, поэтому непонятно на что вы нашли ответ.
В текущей статье сформулировал более доступные определения и то, почему они полностью соответствует определению, приведенному Мартином, объяснил в комментарии https://habr.com/ru/articles/872078/comments/#comment_27767816
Книгу Роберта Мартина не так просто читать, и для того, чтобы понять, что именно имеет ввиду автор, порой, нужно по 10 раз перечитывать каждую строку. Отчасти поэтому ведется столько споров вокруг принципов SOLID.
Аналогии нужны для того, чтобы доступнее объяснять сухие академические термины. Как видно по комментариям к данной статье, даже ссылаясь на эти академические термины, смысл их многим не понятен.
Это формулировка совершенно не противоречит тому, что сказано в текущей статье про определение МВУ/МНУ, а наоборот, подтверждает корректность определений.
Следите за руками:
Переводим:
Будем рассматривать "расстояние" только от ввода, т.к. для вывода все аналогично.
Представим ввод в виде функции
input()
. Функцияinput()
является крайней в стеке вызовов, или выражаясь аналогией из статьи, является последней в “пищевой цепочке”.Теперь, давайте введем две функции:
потребитель()
ипотребляемый()
. Из функциипотребитель()
вызывается функцияпотребляемый()
.Т.е.
потребитель()
использует/вызывает/потребляет реализацию функциипотребляемый()
.Мы должны определить исходя из вышеупомянутого определения Мартина, какая из функций является высокоуровневой, а какая низкоуровневой.
Посмотрим на стек вызовов:
Исходя из определения Мартина, высокоуровневая функция та, до которой расстояние больше. Из изображения выше видно и очевидно, что расстояние
от input() до потребитель()
всегда будет больше, чем расстояниеот input() до потребляемый()
и неважно сколько междупотребитель()-потребляемый()
ипотребляемый()-input()
будет промежуточных вызовов/абстракций. Поэтому в нашем случае,потребитель()
— высокоуровневая функция, апотребляемый()
— низкоуровневая.Ну и уже, надеюсь, очевидное из сказанного/показанного выше, которое просто переведем:
Переводим:
Возвращаясь к определениям из текущей статьи и исходя из вышесказанного можно смело утверждать, что расстояние до МВУ (до потребителя реализаций другого модуля) будет всегда больше, чем до МНУ, а значит определение из текущей статьи всегда будет соответствовать определению, приведенную Мартином.
Сама по себе
либа
— это МВУ, т.к. зависит сама от себя и ей безразлично на код, который она будет использовать.Уровни модулей определяются тем, кто чью реализацию вызывает/использует/потребляет, а не тем, чем код является (либой или фреймворком)
Это не ТГ канал) https://itkartoha.com
Речь шла об аналогии:
и здесь соглашусь, аналогию можно понять двояко.
Но «костяк» описания более точный:
Это совсем не очевидно и по комментариям к статье видно, что многие поняли о чем речь. Если кто-то не так понял термины, то значит формулировка в статье не идеальна.
Вы снова говорите про зависимость, которая, да, инвертировалась, как и должна была. Об этом более детально уже писал в комментарии выше.
А к чему вы привязываете "верхний" и "нижний" уровни? Какая у вас формулировка термина?
Тогда каким образом зависимость инвертируется (поменяет свое направление на противоположное), какая конкретно это будет зависимость (которая ближе к
МВУ
или кМНУ
) и почему именно она?Очевидно, что библиотеки не должны реализовывать
какие-то левые интерфейсы
.Библиотека сама по себе является
МВУ
, т.е. самодостаточна и не зависят от клиентского кода. И если вы не хотите, чтобы в вашем клиентскомМВУ
(который не библиотека) использовалась/вызывалась какая-то библиотека, то вызов этой библиотеки нужно сделать вМНУ
(другими словами, строить обертку над библиотекой).Т.е. если вы называете подключаемую стороннюю библиотеку модулем, то у вас в итоге 3 модуля: библиотека (
МВУ
), клиентскийМВУ
иМНУ
, где вы «изолируете» библиотеку.Но это уже частное решение какой-то задачи с использованием DIP, а не DIP
Корректнее будет DIP — dependency inversion principle
Аналогию с папкой привел для простоты (как и сказано в статье) понимания. Если для кого-то это создает путаницу, то, вы правы, и, вероятно, есть более доходчивые формулировки.
МВУ и МНУ могут быть и классами и лежать в одной директории, и даже находиться в одном файле
Почему B (
МНУ
) не должен зависеть от A (МВУ
)? Про это в DIP не говорится. Мы инвертировали зависимость, и если раньше A зависел от B, то теперь B зависит от A.Хорошее замечание.
Но тогда, где будет инверсия зависимости? Т.е., в этом случае название принципа, как-будто бы не имеет отношения к его содержанию.
Это и называется инверсией зависимости. Т.е. зависимость поменяла свое направление. Раньше A зависел от B, а после применения принципа B зависит от A. Кто-то от кого-то в любом случае будет зависеть
Модули не поменялись местами.
Речь шла про зависимости. Если в модуле нет ни одной строчки кода, это означает, что он «не зависит», а не «не использует».
Здесь аналогично, речь про зависимости, а не «использование реализации»
Как сказано в статье:
Обратите здесь особое внимание на слово «реализацию». Т.е., потребитель реализации как был
МВУ
, так им и остался.Поменялись местами не модули, а зависимости, т.е., другими словами, зависимость инвертировалась, что нам и было нужно.