Контейнери МОК порушують принципи ООП


51

Яке призначення контейнерів МОК? Сукупні причини цього можуть бути спрощені до наступних:

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

Немає способу OOP / SOLID виконати це І мати надзвичайно гарний код.

Якщо це попереднє твердження відповідає дійсності, то як це робити контейнери МОК? Наскільки я знаю, вони не використовують невідому техніку, яку неможливо виконати вручну DI. Тому єдиним поясненням є те, що контейнери IOC порушують принципи OOP / SOLID, використовуючи приватні об'єкти статичних об'єктів.

Чи порушують контейнери МОК такі принципи за кадром? Це справжнє питання, оскільки я добре розумію, але відчуваю, що хтось інший краще розуміє:

  1. Контроль обсягу . Обсяг є причиною майже кожного рішення, яке я приймаю щодо свого дизайну коду. Блок, локальний, модуль, статичний / глобальний. Область застосування повинна бути дуже чіткою, на рівні блоків і якомога менше на статичному рівні. Ви повинні побачити декларації, інстанції та закінчення життєвого циклу. Я довіряю мові та GC для управління сферою, доки я явно нею нею користуюся. У своєму дослідженні я виявив, що контейнери МОК встановлюють більшість або всі залежності як статичні та контролюють їх через деяку реалізацію АОП за кадром. Тож нічого прозорого.
  2. Інкапсуляція . Яка мета інкапсуляції? Чому ми повинні так утримувати приватних членів? З практичних причин це тому, що реалізатори нашого API не можуть порушити реалізацію, змінивши стан (яким повинен керувати клас власника). Але також, з міркувань безпеки, тому ін'єкції не можуть відбутися, що наздоганяють наші країни-члени та обминають перевірку та контроль класу. Так що все (знущання з фреймворків або фреймворків МОК), які якимось чином вводять код перед часом компіляції, щоб дозволити зовнішній доступ приватним членам, є величезним.
  3. Принцип єдиної відповідальності . На перший погляд, контейнери МОК, здається, роблять речі більш чистими. Але уявіть, як би ви зробили те ж саме без рамки для помічників. У вас будуть конструктори, які передають десяток залежностей. Це не означає прикривати це контейнерами МОК, це добре! Це знак перефабрикувати свій код і слідувати SRP.
  4. Відкрити / закрити . Подібно до того, що SRP не є лише для класу (я застосовую SRP вниз до ліній однієї відповідальності, не кажучи вже про методи). Open / Closed - це не просто теорія високого рівня, щоб не змінювати код класу. Це практика розуміння конфігурації вашої програми та контролю над тим, що змінюється, а що розширюється. Контейнери МОК можуть частково змінити спосіб роботи ваших класів, оскільки:

    • а. Основний код не визначає вимикання залежностей, конфігурація рамки є.

    • б. Область може бути змінена за той час, який не контролюється учасниками, що викликають, а натомість визначається статичним фреймом.

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

Причина цього питання полягає в тому, що я не обов'язково є власником усіх контейнерів МОК. І хоча ідея контейнера МОК є приємною, вони здаються просто фасадом, який приховує погану реалізацію. Але для здійснення зовнішнього контролю за сферою дії, доступу приватного члена та ледачого завантаження потрібно продовжувати багато нетривіальних сумнівних речей. AOP - це чудово, але спосіб його досягнення через контейнери IOC також сумнівний.

Я можу довіряти C # та .NET GC робити те, що я цього очікую. Але я не можу покластися на цю саму довіру сторонній інструмент, який змінює мій скомпільований код для вирішення обхідних завдань, які я не зможу зробити вручну.

EG: Entity Framework та інші ORM створюють об'єкти сильно набраних даних і відображають їх в об'єкти бази даних, а також забезпечують основну функціональність плит котла для виконання CRUD. Будь-хто може створити власну ORM та продовжувати дотримуватися принципів OOP / SOLID вручну. Але ці рамки нам допомагають, тому нам не доведеться щоразу винаходити колесо. Тоді як контейнери МОК, схоже, допомагають нам цілеспрямовано працювати над Принципами OOP / SOLID та висвітлювати їх.


13
+1 Кореляція між IOCі Explicitness of codeсаме те, з чим я маю проблеми. Посібник DI легко може стати справжньою справою, але, принаймні, він розміщує в собі одне явне програмне забезпечення в одному місці - "Ви отримуєте те, що бачите, бачите те, що отримуєте". Хоча прив'язки та декларації контейнерів МОК легко можуть стати прихованою паралельною програмою самостійно.
Євген Підскаль

6
Як це навіть питання?
Стівен

8
Це здається більше схожим на повідомлення в блозі, ніж на питання.
Барт ван Інген Шенау

4
У мене є внутрішня категорія питань, "занадто аргументована", що, звичайно, не є офіційною близькою причиною, але я зволікаю, а іноді навіть голосую, щоб закрити "не справжнє питання". Однак це має два аспекти, і це питання відповідає лише першому. (1) Питання викладає тезу і запитує, чи правильно, (2) запитуючий відповідає на відповіді, які не згодні, відстоюючи вихідну позицію. Відповідь Метью суперечить деякому тому, про що йдеться в запитанні, тому, якщо запитуючий не повзає по всьому, я особисто не класифікую це як сумлінне запитання, хоч із формою "Я правий, чи не так?"
Стів Джессоп

3
@BenAaronson Дякую, Бен. Я намагаюся повідомити те, що я дізнався за допомогою своїх досліджень, і отримую відповіді про природу контейнерів МОК. В результаті мого дослідження я виявив ці принципи порушеними. Найкраща відповідь на моє запитання могла б підтвердити або заперечити, що контейнери МОК можуть якось робити те, що ми не можемо зробити вручну, не порушуючи принципів OOP / SOLID. Через ваші чудові коментарі я відновив своє запитання ще більше прикладів.
Суамер

Відповіді:


35

Я перегляну ваші моменти чисельно, але по-перше, ви повинні бути дуже обережними: не плутайте, як користувач використовує бібліотеку з тим, як бібліотека реалізована . Хорошими прикладами цього є Entity Framework (який ви самі цитуєте як хорошу бібліотеку) та MVC ASP.NET. Обидва вони роблять дуже багато під капотом, наприклад, з відображенням, що абсолютно не вважатиметься хорошим дизайном, якщо ви поширюєте його через щоденний код.

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

Отже, пройти через пункт:

  1. Відразу ми доходимо до того, що я можу лише припустити, є прикладом вище. Ви кажете, що контейнери IOC встановлюють більшість залежностей як статичні. Ну, можливо, якась детальна інформація про те, як вони працюють, включає статичне зберігання (хоча зважаючи на те, що вони, як правило, мають примірник об'єкта на зразок Ninject IKernelяк основного сховища, я сумніваюся навіть у цьому). Але це не ваша турбота!

    Насправді контейнер IoC настільки ж явний за обсягом, як і ін'єкція залежних людей. (Я буду продовжувати порівнювати контейнери IoC з ІД бідного чоловіка, тому що порівнювати їх з жодним ІД не було б несправедливо і заплутано. І, якщо бути зрозумілим, я не використовую "бідного чоловіка" як пейоратив.)

    У ІД бідного чоловіка ви б вручну встановили залежність і ввели її до класу, який їй потрібен. У момент, коли ви будуєте залежність, ви вибираєте, що з нею робити - зберігайте її в локально зміненій змінній, змінній класу, статичній змінній, взагалі не зберігайте її. Ви можете передавати один і той же екземпляр у безліч класів або створити новий для кожного класу. Що б там не було. Суть полягає в тому, щоб побачити, що відбувається, ви дивитесь на точку - можливо, біля кореня програми - де створена залежність.

    А що з контейнером IoC? Ну, ви робите точно так само! Знову ж , йдучи по термінології Ninject, ви подивіться на те, де зв'язування встановлено, і знайти , чи говорить це що - щось на зразок InTransientScope, InSingletonScopeабо що завгодно. Якщо що-небудь, це потенційно зрозуміліше, тому що у вас є код саме там, декларуючи його область, а не потрібно шукати метод відстеження того, що відбувається з певним об'єктом (об’єкт може бути віднесений до блоку, але чи багато разів він використовується в цьому блок або просто один раз, наприклад). Тож, можливо, вас відштовхує думка про необхідність використання функції контейнера IoC, а не сирої мови, щоб диктувати область, але поки ви довіряєте своїй бібліотеці IoC, що вам слід! .

  2. Я досі не знаю, про що ти тут говориш. Чи контейнери IoC розглядають приватні об'єкти як частину їх внутрішньої реалізації? Я не знаю, чому вони це зробили, але знову ж таки, якщо вони будуть, це не ваше питання про те, як реалізована бібліотека, яку ви використовуєте.

    Або, можливо, вони виявляють таку можливість, як впорскування в приватні сетери? Чесно кажучи, я ніколи з цим не стикався, і я сумніваюся, чи справді це спільна риса. Але навіть якщо він є, це простий випадок інструменту, яким можна неправильно користуватися. Пам'ятайте, що навіть без контейнера IoC, це лише кілька рядків коду відображення для доступу та зміни приватної власності. Це те, що ви майже ніколи не повинні робити, але це не означає, що .NET поганий для виявлення можливостей. Якщо хтось настільки очевидно і дико зловживає інструментом, це вина, а не людина.

  3. Кінцева точка тут схожа на 2. Переважна більшість часу контейнери IoC DO використовують конструктор! Інжекція сетеру пропонується для дуже конкретних обставин, коли з певних причин конструкторське введення не може бути використане. Кожен, хто весь час використовує ін'єкцію сетера, щоб приховати, скільки залежностей передається, масово використовує цей інструмент. В цьому НЕ винна інструмент, це їхня сила.

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

    Це особливо вірно, тому що це також те, що ви можете зробити так само легко з DI DI бідолахи:

    var myObject 
       = new MyTerriblyLargeObject { DependencyA = new Thing(), DependencyB = new Widget(), Dependency C = new Repository(), ... };
    

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

  4. Зміна способів спільної роботи ваших занять не є порушенням OCP. Якби це було, то вся інверсія залежності сприяла б порушенням OCP. Якби це було так, вони не були б обома однаковою абревіатурою SOLID!

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

    Єдине, про що я можу здогадатися, це те, що ви думаєте, що OCP має щось спільне з поведінкою, яка не змінюється під час виконання, або з того, звідки в коді контролюється життєвий цикл залежностей. Це не. OCP не потребує модифікації існуючого коду, коли вимоги додаються або змінюються. Вся справа в написанні коду, а не в тому, як склеєний вами код (хоча, звичайно, нещільне з'єднання є важливою частиною досягнення OCP).

І ще одне останнє, що ви говорите:

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

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

Додаток

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

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

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

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

void Bind(Type interfaceType, Type concreteType, bool singleton);
T Resolve<T>();

Bindкаже, "де ви бачите аргумент типу конструктора interfaceType, передайте екземпляр concreteType". Додатковий singletonпараметр говорить про те, чи потрібно використовувати один і той же примірник concreteTypeкожного разу або завжди робити новий.

Resolveпросто спробує побудувати Tз будь-яким конструктором, який він може виявити, чиї аргументи - всі типи, які раніше були пов'язані. Він також може називати себе рекурсивно, щоб вирішити залежності до кінця. Якщо він не може вирішити екземпляр, оскільки не все було пов'язано, він видає виняток.

Ви можете спробувати реалізувати це самостійно, і вам здасться, що вам потрібно трохи роздумувати, а також кешувати прив’язки, де singletonце правда, але, звичайно, нічого різкого чи жахливого. І як тільки ви закінчите - вуаля , у вас є ядро ​​вашого власного контейнера IoC! Невже це так страшно? Єдина реальна різниця між цим і Ninject або StructureMap або Castle Windsor або будь-яким іншим, який ви віддаєте перевагу, полягає в тому, що вони мають набагато більше функціональних можливостей для покриття (багатьох!) Випадків використання, коли цієї базової версії було б недостатньо. Але в основі є те, що у вас є суть контейнера IoC.


Дякую Бен. Я, чесно кажучи, працював з контейнерами МОК близько року, і хоча цього не роблю, підручники та колеги навколо мене дійсно зосереджуються на ін'єкції майна (включаючи приватне). Це божевільно, і воно піднімає всі моменти, які я викладаю. Але навіть якщо в цій ситуації потрапляють інші, я думаю, що найкраще, що вони можуть зробити, - це дізнатися більше про те, як контейнери МОК повинні працювати, щоб дотримуватися принципів, і вказати на ці принципи, які порушують принципи, в оглядах кодів. Однак ... (продовження)
Суамер

Моя найбільша стурбованість не обов'язково перерахована в принципах OOP / SOLID, і це явність сфери застосування. Я все-таки ми викидаємо блокове, локальний, модульний та глобальний рівень розуміння області з вікна для ключового слова. Вся ідея використання блоків та декларацій, що визначають, коли щось випадає із сфери застосування чи розміщення, для мене величезна, і я думаю, що це було найстрашнішою частиною контейнерів МОК, щоб замінити цю саму основну частину розробки ключовим словом розширення .
Суамер

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

2
Що стосується 4, то введення залежностей не в абревіатурі SOLID . 'D' = 'Принцип інверсії залежності', що є абсолютно неспорідненою концепцією.
астрельцов

@astr Добре. Я замінив "ін'єкцію" на "інверсію", оскільки я думаю, що саме це я мав намір написати в першу чергу. Це ще трохи надмірного спрощення, оскільки інверсія залежності не обов'язково означає декілька конкретних реалізацій, але я думаю, що сенс досить зрозумілий. Залежно від поліморфної абстракції було б небезпечно, якби відключення реалізації було порушенням OCP.
Бен Аронсон

27

Хіба контейнери МОК не порушують багато цих принципів?

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

На практиці? Абсолютно.

Я маю на увазі, я чув, що не декілька програмістів говорять про те, що вони використовують контейнери IoC спеціально, щоб дозволити вам використовувати певну конфігурацію для зміни поведінки системи - порушення принципу відкритого / закритого типу. Раніше були жарти з приводу кодування в XML, оскільки 90% їх роботи встановлювали конфігурацію Spring. І ви, звичайно, правильно, що приховування залежностей (що, правда, не всі контейнери IoC роблять) - це швидкий і простий спосіб зробити сильно зв'язаний і дуже крихкий код.

Погляньмо на це по-іншому. Розглянемо дуже базовий клас, який має єдину, основну залежність. Це залежить від Action, делегата або функтора, який не приймає жодних параметрів і повертається void. Яка відповідальність цього класу? Ви не можете сказати. Я міг би назвати цю залежність чимось зрозумілим, CleanUpActionщо змушує вас думати, що вона робить те, що ви хочете. Як тільки IoC вступає в гру, вона стає чим завгодно . Будь-хто може ввійти в конфігурацію та змінити ваше програмне забезпечення, щоб зробити дуже багато довільних речей.

"Чим це відрізняється від передачі в Actionконструктор?" Ви запитаєте. Це інакше, тому що ви не можете змінити те, що передається в конструктор після розгортання програмного забезпечення (або під час виконання!). Це інакше, тому що ваші тестові одиниці (або випробування одиниць споживачів) охоплювали сценарії передачі делегата в конструктор.

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

"Чим це відрізняється від використання сценаріїв для керування речами? Це абсолютно чудово робити". хтось із вас запитує. Це не інше. З точки зору дизайну програми, різниці немає. Ви закликаєте програму запускати довільний код. І звичайно, це робиться в іграх для віків. Це робиться в тоннах систем адміністрування систем. Це OOP? Не зовсім.

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


3
Правильно. І багато магазинів стверджують, що їхній товар настільки складний, що вони потрапляють до цієї категорії, коли вони насправді цього не роблять. Як і правда з багатьма сценаріями.
Suamere

12
Але IoC якраз протилежний тому, що ви сказали. Тут використовується відкритість / закритість програми. Якщо ви можете змінити поведінку всієї програми, не змінюючи сам код.
Ейфорія

6
@Euphoric - відкритий для розширення, закритий для модифікації. Якщо ви можете змінити поведінку всієї програми, вона не закрита для модифікації, чи не так? Конфігурація IoC все ще є вашим програмним забезпеченням, це все ще код, який використовуються вашими класами для роботи - навіть якщо він знаходиться в XML, а не Java або C # або будь-якому іншому.
Теластин

4
@Telastyn "Закритий для модифікації" означає, що не слід змінювати (змінювати) код, змінювати поведінку цілого. З будь-якою зовнішньою конфігурацією ви можете просто вказати його на новий dll і мати поведінку цілої зміни.
Ейфорія

12
@Telastyn Про це не говорить принцип відкритого / закритого типу. В іншому випадку метод, який приймає параметри, порушив би OCP, тому що я міг "змінити" його поведінку, передавши йому різні параметри. Так само, як і у вашій версії OCP, це було б в прямому конфлікті з DI, що зробило б досить дивним те, що обидва фігурують в абревіатурі SOLID.
Бен Ааронсон,

14

Чи обов'язково використання контейнера IoC порушує принципи OOP / SOLID? Ні .

Чи можете ви використовувати контейнери IoC таким чином, щоб вони порушували принципи OOP / SOLID? Так .

Контейнери IoC - це лише рамки для управління залежностями у вашій програмі.

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

Наприклад, ASP.NET WebForms, вам потрібно використовувати ін'єкцію властивостей, оскільки це неможливо створити Page. Це зрозуміло, оскільки WebForms ніколи не розроблявся із суворими парадигмами OOP.

З іншого боку, ASP.NET MVC цілком можливо використовувати контейнер IoC, використовуючи інжекцію конструктора OOP / SOLID.

У цьому сценарії (використовуючи конструкторський впорскування), я вважаю, що він сприяє принципам SOLID з наступних причин:

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

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

  • Принцип заміни Ліскова - не дуже актуальний

  • Принцип розбиття інтерфейсу - Ви можете призначити кілька інтерфейсів за один конкретний час, якщо хочете, будь-який контейнер IoC, вартій зерна солі, легко це зробить.

  • Інверсія залежності - контейнери IoC дозволяють легко робити інверсію залежності.

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

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

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

У цьому вся причина OOP / SOLID, щоб зробити системи керованими. Використання контейнера IoC відповідає цій меті.

Письменник цього класу каже: "Якби ми не використовували контейнер МОК, у конструктора було б десяток параметрів !!! Це жахливо!"

Клас з 12 залежностями, ймовірно, є порушенням SRP, незалежно від того, в якому контексті ви його використовуєте.


2
Відмінна відповідь. Крім того, я думаю, що всі основні контейнери МОК також підтримують AOP, що може багато допомогти з OCP. Що стосується наскрізних проблем, AOP, як правило, є більш сприятливим для OCP маршрутом, ніж декоратор.
Бен Ааронсон,

2
@BenAaronson Враховуючи, що AOP вводить код у клас, я б не називав це більш сприятливим для OCP. Ви насильно відкриваєте та змінюєте клас під час компіляції / виконання.
Doval,

1
@Doval Я не бачу, як це вводити код у клас. Зауважте, я говорю про стиль Castle DynamicProxy, де у вас є перехоплювач, а не стиль ІЛ-плетіння. Але перехоплювач - це по суті лише декоратор, який використовує деяке відображення, щоб не довелося з'єднуватися з певним інтерфейсом.
Бен Аронсон

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

Я дійсно не отримую логіки пояснень, що щось не є поганим виходячи з того факту, що це рамка. ServiceLocators також зроблені як рамки, і вони вважаються поганими :).
ipavlu

5

Хіба контейнери МОК не ламаються і не дозволяють кодерам ламатись, багато принципів OOP / SOLID?

staticКлючове слово в C # дозволяє розробникам розбити багато ООП / ТВЕРДИХ принципи , але він по - , як і раніше є дійсним інструментом в правильних обставинах.

Контейнери IoC дозволяють добре структурувати код і зменшити когнітивний тягар для розробників. Контейнер IoC може бути використаний для полегшення дотримання принципів SOLID. Це можна зловживати, звичайно, але якби ми виключили використання будь-якого інструменту, який може бути неправильно використаний, нам доведеться взагалі відмовитися від програмування.

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


Як я розумію, контейнери МОК, що для управління обсягом, лінню та доступністю, він змінює складений код, щоб робити речі, які порушують OOP / SOLID, але приховує цю реалізацію в приємних рамках. Але так, це додатково відкриває інші двері для неправильного використання. Найкращою відповіддю на моє запитання було б якесь уточнення щодо того, як реалізуються контейнери МОК, що додає або заперечує моє власне дослідження.
Суамер

2
@Suamere - Я думаю, ви можете використовувати занадто широке визначення МОК. Я використовую не змінює складений код. Існує безліч інших інструментів, що не включають МОК, які також змінюють складений код. Можливо, ваш заголовок повинен бути більше схожим: "Чи змінюється зміна складеного коду ...".
Серад

@Suamere Я не маю такого досвіду з IoC, але я ніколи не чув, щоб IoC міняв скомпільований код. Надайте, будь ласка, інформацію про платформи / мови / рамки, про які ви говорите.
Ейфорія

1
"Контейнер IoC може бути використаний для полегшення дотримання принципів SOLID." - Ви можете, будь ласка, детальніше розробити? Пояснення, наскільки конкретно це допомагає кожному принципу, було б корисним. І, звичайно, використовувати щось не так, як допомагати щось статися (наприклад, DI).
День

1
@Euphoric Я не знаю про версію фреймворків .net, про яку говорив, але Spring, безумовно, робить модифікацію коду під кришкою. Найбільш очевидним прикладом є підтримка методу ін'єкцій на основі методу (за допомогою якого він переосмислює абстрактний метод у вашому класі для повернення залежності, якою він керує). Він також широко використовує автоматично створені проксі-сервери, щоб обернути ваш код, щоб забезпечити додаткову поведінку (наприклад, для автоматичного управління транзакціями). Однак, багато з цього насправді не пов'язане з його роллю в рамках проекту DI.
Жуль

3

Я бачу багато помилок і непорозумінь у вашому запитанні. Перший головний - це те, що вам не вистачає різниці між властивістю / полем впорскування, введенням конструктора та локатором обслуговування. Було багато дискусій (наприклад, вогняних вогнів) про те, який саме спосіб робити. У вашому запитанні ви, мабуть, припускаєте лише введення властивостей та ігноруєте введення конструктора.

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

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

Отже, відповідь на ваше запитання - ні. Контейнери IoC не порушують принципів проектування OOP. Зовсім навпаки, вони вирішують проблеми, викликані дотриманням принципів SOLID (особливо принципу інверсії залежності).


Нещодавно спробував застосувати IoC (Autofac) до нетривіального проекту. Зрозумівши, що мені довелося виконати аналогічну роботу з немовними конструкціями (new () vs API): уточнення того, що слід вирішити для інтерфейсів і що слід вирішити для конкретних класів, вказавши, що має бути екземпляром і що повинно бути однотонним, переконайтеся, що введення властивостей працює правильно, щоб полегшити кругову залежність. Вирішив відмовитися. Хоча добре працює з контролерами в ASP MVC.
День

@Den Ваш проект, очевидно, не був розроблений з урахуванням інверсії залежності. І ніде я не говорив, що використання IoC - тривіально.
Ейфорія

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

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