All streams
Search
Write a publication
Pull to refresh
18
0
АйТиКартоха @itkartoha

User

Send message

Оба модуля при добавлении абстракции/контракта/интерфейса в МВУ, после которого МВУ не зависит от МНУ,  будут зависеть от абстракций по определению. МВУ будет зависеть от собственного интерфейса, а МНУ от интерфейса МВУ.

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

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

Открываем википедию и видим ответ:

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

В текущей статье сформулировал более доступные определения и то, почему они полностью соответствует определению, приведенному Мартином, объяснил в комментарии 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

DI - Dependency Inversion

Корректнее будет DIP — dependency inversion principle

С автором статьи и его пониманием модуля как папки и вводом границ не согласен. На мой взгляд это только вносит ещё большую путаницу.

Аналогию с папкой привел для простоты (как и сказано в статье) понимания. Если для кого-то это создает путаницу, то, вы правы, и, вероятно, есть более доходчивые формулировки.
МВУ и МНУ могут быть и классами и лежать в одной директории, и даже находиться в одном файле

На схеме отмеченной галочкой видимо изображен верный вариант. Но разве в этом случае не зависит ли класс B от интерфейса, а следовательно и от модуля верхнего уровня, от которого зависеть не должен?

Почему B (МНУ) не должен зависеть от A (МВУ)? Про это в DIP не говорится. Мы инвертировали зависимость, и если раньше A зависел от B, то теперь B зависит от A.

Оба типа модулей отдельно, а абстракции отдельно.

Хорошее замечание.
Но тогда, где будет инверсия зависимости? Т.е., в этом случае название принципа, как-будто бы не имеет отношения к его содержанию.

однако, меня сильно смущает, что теперь модуль, в котором находится В не может существовать без модуля, где поселился А и интерфейс, по той простой причине, что В должен реализовывать этот самый интерфейс ?!

Это и называется инверсией зависимости. Т.е. зависимость поменяла свое направление. Раньше A зависел от B, а после применения принципа B зависит от A. Кто-то от кого-то в любом случае будет зависеть

Модули не поменялись местами.

Но если в "МВУ" нет "ни одной строчки кода из МНУ", то... он его не использует.

Речь шла про зависимости. Если в модуле нет ни одной строчки кода, это означает, что он «не зависит», а не «не использует».

При этом поскольку ваш "МНУ" использует интерфейс из "МВУ", он... начал его использовать. У вас модули поменялись местами.

Здесь аналогично, речь про зависимости, а не «использование реализации»

Как сказано в статье:

Модуль верхнего уровня (далее МВУ) — это модуль, который в конечном итоге использует реализацию другого модуля

Обратите здесь особое внимание на слово «реализацию». Т.е., потребитель реализации как был МВУ, так им и остался.
Поменялись местами не модули, а зависимости, т.е., другими словами, зависимость инвертировалась, что нам и было нужно.

2

Information

Rating
Does not participate
Registered
Activity