Давайте посмотрим на это практически

Obj 3теперь знает, Obj 4существует. Ну и что? Почему мы заботимся?
DIP говорит
«Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций».
Хорошо, но не все ли абстракции объектов?
DIP также говорит
«Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций».
Хорошо, но если мой объект правильно инкапсулирован, разве это не скрывает какие-либо детали?
Некоторые люди любят слепо настаивать на том, что каждому объекту нужен интерфейс с ключевыми словами. Я не один из них. Мне нравится слепо настаивать на том, что если вы не собираетесь использовать их сейчас, вам нужен план, чтобы справиться с чем-то вроде них позже.
Если ваш код полностью поддерживает рефакторинг в каждом выпуске, вы можете просто извлечь интерфейсы позже, если они вам понадобятся. Если вы опубликовали код, который не хотите перекомпилировать, и обнаружите, что хотите поговорить через интерфейс, вам понадобится план.
Obj 3знает, что Obj 4существует. Но Obj 3знает ли Obj 4бетон?
Вот почему вот так приятно НЕ распространяться newповсюду. Если Obj 3не знает, Obj 4конкретен ли он, скорее всего потому, что он его не создал, тогда, если вы прокрались позже и превратились Obj 4в абстрактный класс, Obj 3все равно.
Если вы можете сделать это, то Obj 4все время был полностью абстрактным. Единственное, что создает интерфейс между ними с самого начала, - это уверенность в том, что кто-то случайно не добавит код, который выдает Obj 4конкретный прямо сейчас. Защищенные конструкторы могут снизить этот риск, но это приводит к другому вопросу:
Есть ли Obj 3 и Obj 4 в одной упаковке?
Объекты часто так или иначе группируются (пакет, пространство имен и т. Д.). При разумной группировке более вероятно изменение воздействия внутри группы, а не между группами.
Мне нравится группировать по характеристикам. Если Obj 3и Obj 4находятся в той же группе и слой это очень маловероятно , что вы опубликовали один и не хотите , чтобы реорганизовать его в то время как требуется изменять только другой. Это означает, что эти объекты с меньшей вероятностью получат выгоду от размещения абстракции между ними до того, как в этом возникнет явная необходимость.
Если вы пересекаете границу группы, это действительно хорошая идея, чтобы объекты с обеих сторон изменялись независимо друг от друга.
Это должно быть так просто, но, к сожалению, и Java, и C # сделали неудачный выбор, который усложняет это.
В C # принято называть каждый интерфейс ключевого слова Iпрефиксом. Это заставляет клиентов ЗНАТЬ, что они говорят с интерфейсом ключевого слова. Это портит план рефакторинга.
В Java традиционно используется лучший шаблон именования: FooImple implements Fooоднако это помогает только на уровне исходного кода, поскольку Java компилирует интерфейсы ключевых слов в другой двоичный файл. Это означает, что при рефакторинге Fooот конкретных к абстрактным клиентам, которым не требуется ни одного измененного символа, все равно придется перекомпилироваться.
Именно эти ОШИБКИ на этих конкретных языках не позволяют людям откладывать формальное абстрагирование до тех пор, пока им это действительно не нужно. Вы не сказали, какой язык используете, но понимаете, что есть языки, у которых просто нет этих проблем.
Вы не сказали, какой язык вы используете, поэтому я просто призываю вас тщательно проанализировать ваш язык и ситуацию, прежде чем вы решите, что это будут ключевые интерфейсы везде.
Принцип ЯГНИ играет здесь ключевую роль. Но так же: «Пожалуйста, сделайте так, чтобы мне было трудно выстрелить себе в ногу».