Рассматривая программное обеспечение среднего размера с n-уровневой архитектурой и внедрением зависимостей, я могу с уверенностью сказать, что объект, принадлежащий слою, может зависеть от объектов более низких уровней, но никогда от объектов более высоких уровней.
Но я не уверен, что думать об объектах, которые зависят от других объектов того же слоя.
В качестве примера, давайте предположим, что приложение с тремя слоями и несколькими объектами, такими как один на изображении. Очевидно, что сверху вниз (зеленые стрелки) в порядке, снизу вверх (красная стрелка) не в порядке, но как насчет зависимости внутри того же слоя (желтая стрелка)?
За исключением круговой зависимости, мне любопытно узнать о любой другой проблеме, которая может возникнуть, и о том, насколько многоуровневая архитектура нарушается в этом случае.
источник
Ответы:
Да, объекты в одном слое могут иметь прямые зависимости друг от друга, иногда даже циклические - именно это и делает основное отличие от разрешенных зависимостей между объектами в разных слоях, где либо не разрешены прямые зависимости, либо просто строгая зависимость направление
Однако это не означает, что они должны иметь такие зависимости произвольным образом. На самом деле это зависит от того, что представляют ваши слои, насколько велика система и какова ответственность частей. Обратите внимание, что «многоуровневая архитектура» является неопределенным термином, и существует огромное разнообразие того, что это на самом деле означает в различных типах систем.
Например, предположим, что у вас есть «горизонтально-слоистая система» с уровнем базы данных, бизнес-уровнем и уровнем пользовательского интерфейса (UI). Допустим, что слой пользовательского интерфейса содержит как минимум несколько десятков различных диалоговых классов.
Можно выбрать дизайн, в котором ни один из классов диалога не зависит напрямую от другого класса диалога. Можно выбрать дизайн, в котором существуют «главные диалоги» и «вспомогательные диалоги», и существуют только прямые зависимости от «основных» к «вспомогательным» диалогам. Или можно предпочесть дизайн, в котором любой существующий класс пользовательского интерфейса может использовать / повторно использовать любой другой класс пользовательского интерфейса из того же уровня.
Это все возможные варианты дизайна, возможно, более или менее разумные в зависимости от типа системы, которую вы строите, но ни один из них не делает «слоистость» вашей системы недействительной.
источник
Если честно, я не думаю, что вам должно быть удобно с этим. Имея дело с чем угодно, кроме тривиальной системы, я хотел бы обеспечить, чтобы все слои зависели только от абстракций других слоев; и ниже, и выше.
Так например,
Obj 1
не должно зависеть отObj 3
. Он должен зависеть, например,IObj 3
и должен быть указан, с какой реализацией этой абстракции он будет работать во время выполнения. То, что делает рассказывание, не должно быть связано ни с одним из уровней, так как его работа состоит в том, чтобы отобразить эти зависимости. Это может быть контейнер IoC, пользовательский код, называемый, например,main
который использует чистый DI. Или на толчке это может быть даже сервисный локатор. Независимо от этого, зависимости не существуют между слоями, пока эта вещь не обеспечивает отображение.Я бы сказал, что это единственный раз, когда у вас должны быть прямые зависимости. Он является частью внутренней работы этого слоя и может быть изменен, не затрагивая другие слои. Так что это не вредная связь.
источник
Давайте посмотрим на это практически
Obj 3
теперь знает,Obj 4
существует. Ну и что? Почему мы заботимся?Хорошо, но не все ли абстракции объектов?
Хорошо, но если мой объект правильно инкапсулирован, разве это не скрывает какие-либо детали?
Некоторые люди любят слепо настаивать на том, что каждому объекту нужен интерфейс с ключевыми словами. Я не один из них. Мне нравится слепо настаивать на том, что если вы не собираетесь использовать их сейчас, вам нужен план, чтобы справиться с чем-то вроде них позже.
Если ваш код полностью поддерживает рефакторинг в каждом выпуске, вы можете просто извлечь интерфейсы позже, если они вам понадобятся. Если вы опубликовали код, который не хотите перекомпилировать, и обнаружите, что хотите поговорить через интерфейс, вам понадобится план.
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
от конкретных к абстрактным клиентам, которым не требуется ни одного измененного символа, все равно придется перекомпилироваться.Именно эти ОШИБКИ на этих конкретных языках не позволяют людям откладывать формальное абстрагирование до тех пор, пока им это действительно не нужно. Вы не сказали, какой язык используете, но понимаете, что есть языки, у которых просто нет этих проблем.
Вы не сказали, какой язык вы используете, поэтому я просто призываю вас тщательно проанализировать ваш язык и ситуацию, прежде чем вы решите, что это будут ключевые интерфейсы везде.
Принцип ЯГНИ играет здесь ключевую роль. Но так же: «Пожалуйста, сделайте так, чтобы мне было трудно выстрелить себе в ногу».
источник
В дополнение к ответам выше, я думаю, что это может помочь вам взглянуть на это с разных точек зрения.
Например, с точки зрения правила зависимости . DR - это правило, предложенное Робертом К. Мартином для его знаменитой « Чистой архитектуры» .
Это говорит
Под политикой более высокого уровня он подразумевает абстракции более высокого уровня . Компоненты, которые дают утечку в деталях реализации, например, интерфейсы или абстрактные классы в отличие от конкретных классов или структур данных.
Дело в том, что правило не ограничивается межуровневой зависимостью. Это только указывает на зависимость между частями кода, независимо от местоположения или уровня, к которому они принадлежат.
Так что нет, нет ничего плохого в том, чтобы иметь зависимости между элементами одного слоя. Тем не менее, зависимость все еще может быть реализована для передачи с принципом стабильной зависимости .
Другая точка зрения - SRP.
Разделение - это наш способ избавиться от вредных зависимостей и поделиться с ними некоторыми лучшими практиками, такими как инверсия зависимостей (IoC). Однако те элементы, у которых есть общие причины изменения, не дают поводов для разделения, потому что элементы с одной и той же причиной изменения будут меняться в одно и то же время (очень вероятно), и они будут развернуты в то же время. Если это так , между
Obj3
иObj4
затем, еще раз, нет ничего плохого.источник