Чи проблематично існувати залежність між об'єктами одного шару в багатошаровій архітектурі програмного забезпечення?


12

Розглядаючи середньо велике програмне забезпечення з архітектурою n-шару та ін'єкцією залежностей, мені зручно сказати, що об’єкт, що належить до шару, може залежати від об'єктів із нижчих шарів, але ніколи від об'єктів з більш високих шарів.

Але я не впевнений, що думати про об’єкти, які залежать від інших об’єктів того ж шару.

Як приклад, припустимо додаток з трьома шарами та кількома об’єктами, як той, що на зображенні. Очевидно, що залежності від верху вниз (зелені стрілки) не в порядку, знизу вгору (червона стрілка) не все в порядку, а як щодо залежності всередині того ж шару (жовта стрілка)?

введіть тут опис зображення

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


Якщо у вас немає циклів, у шарі, де у вас є горизонтальні зв’язки, існує розщеплення на підшари, які мають лише залежність між шарами
max630

Відповіді:


10

Так, об'єкти в одному шарі можуть мати прямі залежності між собою, іноді навіть циклічні - саме це і є тим, що робить основну різницю від дозволених залежностей між об'єктами в різних шарах, де або не допускаються прямі залежності, або просто сувора залежність напрямок.

Однак це не означає, що вони повинні мати такі залежності довільно. Насправді це залежить від того, що представляють ваші шари, наскільки великою є система та якою має бути відповідальність деталей. Зауважимо, що "шарувата архітектура" - це невиразний термін, існує величезна різниця у тому, що це насправді означає в різних системах.

Наприклад, скажімо, що у вас є "горизонтально шарувана система" з шаром бази даних, бізнес-шаром та шаром користувальницького інтерфейсу (UI). Скажімо, шар користувальницького інтерфейсу містить щонайменше кілька десятків різних діалогових класів.

Можна вибрати дизайн, коли жоден з діалогових класів безпосередньо не залежить від іншого діалогового класу. Можна вибрати дизайн, в якому існують "основні діалоги" та "піддіалоги" та існують лише прямі залежності від "головного" до "під" діалогового вікна. Або можна віддати перевагу дизайну, коли будь-який існуючий клас інтерфейсу може використовувати / використовувати повторно будь-який інший клас інтерфейсу з того ж шару.

Це всі можливі варіанти дизайну, можливо більш-менш розумні в залежності від типу системи, яку ви будуєте, але жодна з них не робить "пластовість" вашої системи недійсною.


Продовжуючи на прикладі користувальницького інтерфейсу, які плюси і мінуси мають внутрішні залежності? Я бачу, що це полегшує повторне використання та введення циклічних залежностей, що може бути проблемою залежно від використовуваного методу DI. Ще щось?
bracco23

@ bracco23: плюси і мінуси наявності "внутрішніх" залежностей такі ж, як і плюси мінусів довільних залежностей. "Мінуси" - це те, що вони ускладнюють повторне використання компонентів і важче тестують, особливо у відриві від інших компонентів. Плюси полягають у тому, що явні залежності роблять речі, які потребують згуртованості, простішими у використанні, розумінні, управлінні та тестуванні.
Док Браун

14

Мені зручно сказати, що об'єкт, що належить до шару, може залежати від об'єктів із нижчих шарів

Якщо чесно, я не думаю, що вам це має бути комфортно. Маючи справу з чим-небудь, крім тривіальної системи, я б прагнув забезпечити, щоб усі шари залежали лише від абстракцій інших шарів; і нижчі, і вищі.

Так, наприклад, Obj 1не повинно залежати від Obj 3. Він повинен залежати від, наприклад, IObj 3і слід сказати, з якою реалізацією цієї абстракції слід працювати під час виконання. Те, що робить розповідь, не повинно бути пов'язане з будь-яким із рівнів, оскільки це завдання - це зіставити ці залежності. Це може бути контейнер IoC, призначений для користувача код, наприклад main, використовуючи чистий DI. Або з поштовху це може бути навіть сервіс-локатор. Незалежно від цього, існують залежності між шарами, поки ця річ не надасть відображення.

Але я не впевнений, що думати про об’єкти, які залежать від інших об’єктів того ж шару.

Я б заперечував, що це єдиний раз, коли ти маєш мати прямі залежності. Це частина внутрішньої роботи цього шару і може бути змінена, не впливаючи на інші шари. Тож це не шкідлива муфта.


Дякую за відповідь. Так, шар не повинен піддавати об'єктам, а інтерфейсам, я це знаю, але це я просто залишив для простоти.
bracco23

4

Давайте розглянемо це практично

введіть тут опис зображення

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з конкретних на абстрактних клієнтів, яким не потрібен жоден символ зміненого коду, все одно потрібно перекомпілювати.

Саме БУГИ на цих конкретних мовах не дозволяють людям відкладати формальне абстрагування, поки їм це справді не потрібно. Ви не сказали, якою мовою користуєтесь, але зрозуміли, що є деякі мови, у яких просто немає цих проблем.

Ви не сказали, якою мовою ви користуєтесь, тому я просто закликаю вас уважно проаналізувати свою мову та ситуацію, перш ніж ви вирішите, що це буде інтерфейс ключових слів скрізь.

Принцип YAGNI тут відіграє ключову роль. Але так і "Будь ласка, важко застрелити себе в ногу".


0

На додаток до вищезазначених відповідей, я думаю, що це може допомогти вам подивитися на це з різних точок зору.

Наприклад, з точки зору Правила залежності . DR є правилом запропонував Роберт С. Мартін своїй знаменитій чистої архітектури .

Він говорить

Залежності вихідного коду повинні вказувати лише всередину, до політики вищого рівня.

Під політикою вищого рівня він має на увазі абстракції вищого рівня . Компоненти, що просочуються на деталі реалізації, як, наприклад, інтерфейси або абстрактні класи на відміну від конкретних класів або структур даних.

Вся справа в тому, що правило не обмежується міжшаровою залежністю. Він лише вказує на залежність між фрагментами коду, незалежно від місця розташування або шару, до якого вони належать.

Так що ні, немає нічого суттєво поганого у тому, щоб мати залежність між елементами одного шару. Однак залежність все ще може бути реалізована для передачі принципу стабільної залежності .

Інша точка зору - SRP.

Розв'язка - це наш спосіб подолати шкідливі залежності та передати такі найкращі практики, як інверсія залежності (IoC). Однак ті елементи, які поділяють причини зміни, вони не наводять причин для розв’язки, оскільки елементи, що мають однакову причину для зміни, будуть змінюватися одночасно (дуже ймовірно), і вони будуть розгорнуті одночасно. Якщо це так між Obj3і Obj4потім, ще раз, по суті, нічого поганого немає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.