Давайте посмотрим на это практически
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
от конкретных к абстрактным клиентам, которым не требуется ни одного измененного символа, все равно придется перекомпилироваться.
Именно эти ОШИБКИ на этих конкретных языках не позволяют людям откладывать формальное абстрагирование до тех пор, пока им это действительно не нужно. Вы не сказали, какой язык используете, но понимаете, что есть языки, у которых просто нет этих проблем.
Вы не сказали, какой язык вы используете, поэтому я просто призываю вас тщательно проанализировать ваш язык и ситуацию, прежде чем вы решите, что это будут ключевые интерфейсы везде.
Принцип ЯГНИ играет здесь ключевую роль. Но так же: «Пожалуйста, сделайте так, чтобы мне было трудно выстрелить себе в ногу».