Ін'єкційна залежність від заводської структури


498

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

Одного разу хтось сказав мені, що це те, як ти ним користуєшся, має значення!

Колись я використовував StructureMap контейнер DI для вирішення проблеми, пізніше я переробив його для роботи з простою фабрикою та видалив посилання на StructureMap.

Хтось може сказати мені, в чому різниця між ними і де використовувати, що найкраща практика тут?


21
Чи не можуть ці два підходи компліментувати один одного: використовуючи введення залежностей для введення заводських класів?
Річард Єв

20
Було б дуже приємно, якби на це запитання була відповідь з деяким кодом! Я досі не бачу, як DI може бути вигідним / відмінним від використання фабрики для створення? Вам потрібно буде лише замінити цей один рядок у заводському класі, щоб змінити, який об’єкт / реалізація створений?
Гедеон

2
@gideon чи не змусив би вас скласти додаток чи принаймні модуль, що містить заводський клас?
лізергічно-кислотна

1
@liortal так, це правильно. З цього коментаря робив довге дослідження щодо DI, і тепер я розумію, що DI робить заводський метод на крок вперед.
gideon

1
Ознайомтеся з цією чудовою відповіддю: stackoverflow.com/questions/4985455/… - він це дуже добре вимовляє та надає зразки коду.
Луїс Перес

Відповіді:


293

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


172
Шаблон DI не вимагає ніяких рамок. Ви можете зробити DI, вручну написавши фабрики, які роблять DI. DI рамки просто полегшують.
Еско Луонтола

28
@Perpetualcoder - Дякую @Esko - Не захоплюйтесь словом, що означає деяку передову бібліотеку сторонніх розробників.
willcodejavaforfood

4
+1 @willcode спасибі! Тому вам доведеться замість цього змінити файли config / xml. Розумію. Посилання на wiki en.wikipedia.org/wiki/… визначає заводський зразок якManually-Injected Dependency
gideon

5
Я не отримую переваги змінити 1 рядок XML проти зміни 1 рядка коду. Не могли б ви детальніше?
Рафаель Ейн

1
Повторити відповідь ОП, замінивши "DI" на "завод", все ще має сенс.
Едсон Медіна

219

Я б запропонував зберегти поняття зрозумілими та простими. Dependency Injection є більшою мірою архітектурною схемою для слабко з’єднаних компонентів програмного забезпечення. Заводський зразок - лише один із способів розділити відповідальність за створення об'єктів інших класів на іншу сутність. Фабричну схему можна назвати інструментом реалізації DI. Введення залежності може бути реалізовано багатьма способами, як DI, використовуючи конструктори, використовуючи відображення xml-файлів тощо.


4
Це правда, що заводський зразок є одним із способів впровадження введення залежності. Ще однією перевагою використання введення залежності від фабричного шаблону є те, що рамки DI нададуть вам гнучкість щодо того, як зареєструвати ваші абстракції щодо конкретних типів. Такі як код як Config, XML або Auto Configuration. Ця гнучкість дозволить вам керувати терміном експлуатації об'єктів або вирішувати, як і коли зареєструвати свої інтерфейси.
Теоман шипахі

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

1
Гарна відповідь. Багато хто плутає з цим питанням: "Які паттенти я повинен вибрати?" Насправді шаблони дизайну - це лише спосіб вирішити проблему в належних ситуаціях. Якщо ви не знаєте, який шаблон дизайну ви повинні використовувати, або "Де я повинен реалізувати шаблон?" тоді вам цього не потрібно.
Benyi

2
Я думаю, що точніше сказати, що Factory Pattern є інструментом для реалізації IoC (не DI). Зверніть увагу, що DI - це форма IoC
Hooman Bahreini

185

Ін'єкційна залежність

Замість того, щоб миттєво встановлювати деталі, автомобіль просить деталі, які йому потрібні.

class Car
{
    private Engine engine;
    private SteeringWheel wheel;
    private Tires tires;

    public Car(Engine engine, SteeringWheel wheel, Tires tires)
    {
        this.engine = engine;
        this.wheel = wheel;
        this.tires = tires;
    }
}

Заводська

Складає шматки, щоб зробити повний предмет і приховає тип бетону від абонента.

static class CarFactory
{
    public ICar BuildCar()
    {
        Engine engine = new Engine();
        SteeringWheel steeringWheel = new SteeringWheel();
        Tires tires = new Tires();
        ICar car = new RaceCar(engine, steeringWheel, tires);
        return car;
    }   
}

Результат

Як бачите, фабрики та DI доповнюють один одного.

static void Main()
{
     ICar car = CarFactory.BuildCar();
     // use car
}

Ви пам’ятаєте золотистих та трьох ведмедів? Ну, ін'єкція залежності така собі. Ось три способи зробити те саме.

void RaceCar() // example #1
{
    ICar car = CarFactory.BuildCar();
    car.Race();
}

void RaceCar(ICarFactory carFactory) // example #2
{
    ICar car = carFactory.BuildCar();
    car.Race();
}

void RaceCar(ICar car) // example #3
{
    car.Race();
}

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

Приклад №2 - Це трохи краще, тому що тепер ми знаємо, що нам потрібна машина, оскільки ми проїжджаємо на автомобільному заводі. Але цього разу ми проїжджаємо занадто багато, оскільки всім методом насправді потрібен автомобіль. Ми проїжджаємо на заводі просто для того, щоб побудувати машину, коли машину можна було б побудувати за межами методу та передати.

Приклад №3 - Це ідеально, оскільки метод запитує саме те , що йому потрібно. Не надто чи занадто мало. Мені не потрібно писати MockCarFactory лише для створення MockCars, я можу передати макет прямо. Це прямо, і інтерфейс не лежить.

Ця технічна розмова Google від Місько Гевери є дивовижною і є основою того, з чого я взяв свій приклад. http://www.youtube.com/watch?v=XcT4yYu_TTs


1
Заводський зразок вводиться як DI.
Mangesh Pimpalkar

7
@PhilGoetz Те, що ви описуєте, більше нагадує схему "Локатор послуг". Вони мають подібні цілі, оскільки вони прагнуть від'єднати послуги від своїх споживачів. Однак у шаблона служби пошуку послуг є багато недоліків. Головним чином, приховувати залежності - це не дуже добре. Споживач все ще повинен отримати свої залежності, але замість того, щоб визначити чіткий інтерфейс, вони передаються «таємно» і покладаються на глобальний стан. У прикладі споживач поняття про фабрику не має, він запитує лише те, що йому потрібно, і не має стосуватися себе того, як будується ця потреба.
Деспертар

1
@MatthewWhited У більшості дизайнерських рішень є розпродаж. Завдяки DI ви отримуєте гнучкість, прозорість та більш перевірену систему, але ціною оголення кишок. Багато людей вважають це прийнятним. Це не означає, що DI завжди є найкращим рішенням, і це питання не в цьому. Приклад покликаний показати різницю між DI та заводською схемою, показавши деякі хороші та погані використання кожного.
Деспертар

3
Це не модель DI, це лише реалізація IoC. DI використовує ServiceLocator для визначення потрібної залежності та введення її в конструктор під час створення об’єкта, ваш код лише відокремлює створення об’єктів із сфери вашого класу.
Дієго Мендес

3
@DiegoMendes Те, що ви описуєте, є рамкою, яка автоматизує DI. ServiceLocator, як ви його називаєте, робить DI для вас. Я показую сам візерунок, який не потребує фантазійних рам. Див. Stackoverflow.com/a/140655/1160036 або див. En.wikipedia.org/wiki/… : "[список фреймворків] підтримує введення залежності, але не потрібно вводити залежність". Також "Ін'єкція - це передача залежності залежному об'єкту". Ви закінчуєте складність красиво простої концепції.
Деспертар

50

Причина залежності впорскування (DI) та заводські шаблони схожі в тому, що вони є двома реалізаціями Інверсії управління (IoC), яка є архітектурою програмного забезпечення. Простіше кажучи, це два рішення однієї проблеми.

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

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

Шаблон дизайну Фабрики досить старий (з точки зору програмного забезпечення) і існує деякий час. З недавньої популярності архітектурного зразка IoC він відроджується.

Я думаю, що мова йде про схеми дизайну IoC: інжектори впорскують, локатори розміщують, а заводи реконструюють.


3
Це найкраща відповідь ... інші відповіді або не згадують IoC, або не визнають, що DI є формою IoC.
Хоман Бахрейні

Дякую, коли я вперше вивчав МОК, я майже кричав, що це просто інша форма фабричного малюнка, виявляється, вони дійсно схожі між собою
Харві Лін

40

Є проблеми, які легко вирішити за допомогою ін'єкцій залежностей, які не так легко вирішити на наборі заводів.

Деяка різниця між, з одного боку, інверсією управління та введення залежності (IOC / DI), а з іншого боку, локатором обслуговування або набором фабрик (заводів), полягає в:

IOC / DI - це повна екосистема доменних об'єктів і послуг сама по собі. Він налаштовує все для вас у визначеному вами порядку. Об'єкти та послуги вашого домену побудовані контейнером, а не створюють самі: вони не мають ніякої залежності ні від контейнера, ні від заводів. IOC / DI дозволяє надзвичайно високий ступінь конфігурації з усією конфігурацією в одному місці (конструкція контейнера) у верхньому шарі вашої програми (GUI, веб-інтерфейс).

Фабрика конспектує частину побудови ваших доменних об’єктів та послуг. Але доменні об’єкти та послуги все ще відповідають за з'ясування того, як побудувати себе та як отримати все, від чого вони залежать. Усі ці "активні" залежності фільтрують весь рівень вашої програми. Немає єдиного місця, де можна було б налаштувати все.


26

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


22

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

Від: http://tutorials.jenkov.com/dependency-injection/dependency-injection-containers.html


15

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

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

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


14

Теорія

Слід враховувати два важливі моменти:

  1. Хто створює об’єкти

    • [Фабрика]: Ви повинні написати, ЯК об’єкт повинен бути створений. У вас є окремий заводський клас, який містить логіку створення.
    • [Ін'єкція залежності]: У практичних випадках це робиться за допомогою зовнішніх фреймворків (наприклад, у Java, що буде Spring / ejb / guice). Ін'єкція відбувається "магічно" без явного створення нових об'єктів
  2. Якими об’єктами керує:

    • [Завод]: Зазвичай відповідає за створення об'єктів, що належать до стану
    • [Ін'єкції залежностей] Більше шансів створити об'єкти без громадянства

Практичний приклад, як використовувати як заводські, так і ін'єкційні залежності в одному проекті

  1. Що ми хочемо побудувати

Модуль програми для створення замовлення, який містить декілька записів, що називаються порядковим рядком.

  1. Архітектура

Припустимо, ми хочемо створити таку архітектуру шарів:

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

Об'єкти домену можуть бути об'єктами, що зберігаються всередині бази даних. Репозиторій (DAO) допомагає отримувати об'єкти з бази даних. Сервіс надає API для інших модулів. Дозволяє для операцій над orderмодулем

  1. Доменний шар та використання заводів

Суб'єктами, які будуть знаходитися в базі даних, є Order та OrderLine. Замовлення може мати кілька OrderLines. Зв'язок між орденом і орденомLine

Тепер приходить важлива конструкторська частина. Чи повинні модулі поза цим створювати і керувати OrderLines самостійно? Ні. Рядок замовлення повинен існувати лише тоді, коли у вас пов’язаний Порядок. Було б найкраще, якби ви могли приховати внутрішню імплементацію до зовнішніх класів.

Але як створити замовлення без знань про OrderLines?

Заводська

Хтось, хто хоче створити нове замовлення, використовував OrderFactory (який приховає деталі про те, як ми створюємо Order).

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

Ось як це буде виглядати всередині IDE. Класи поза domainпакетом будуть використовувати OrderFactoryзамість конструктора всерединіOrder

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

OrderRepository та OrderService керуються рамками введення залежності. Репозиторій відповідає за керування операціями CRUD на базі даних. Сервіс вводить репозиторій і використовує його для збереження / пошуку правильних класів домену.

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


Чи можете ви уточнити це? [Factory]: Usually responsible for creation of stateful objects [Dependency Injections] More likely to create stateless objectsЯ не розумію, чому
Максим Дмитрієв

2
Об'єкти, що знаходяться у стані, швидше за все, знаходяться в доменному шарі програми, де ви зіставляєте свої дані в базу даних, як правило, там ви не використовуєте введення залежності. Фабрика часто використовується для створення AggregateRoot з дерева складних об'єктів
Marcin Szymczak

12

Я знаю, що це питання давнє, але я хотів би додати свої п'ять центів,

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

Насправді, якщо ви використовуєте весну, наприклад, у вас є можливість автоматичного підключення ресурсів (DI) або щось подібне:

MyBean mb = ctx.getBean("myBean");

А потім використовуйте цей екземпляр 'mb', щоб зробити що-небудь. Це не дзвінок на завод, який поверне вам екземпляр ??

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

І якщо ви запитаєте мене про мою думку (а я знаю, що ви цього не зробили), я вважаю, що DI робить те саме, але лише додає більшої складності розвитку, чому?

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

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

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

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

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

MyBean mb = MyBeanForEntreprise1(); //In the classes of the first enterprise
MyBean mb = MyBeanForEntreprise2(); //In the classes of the second enterprise

те саме, що це:

@Autowired MyBean mbForEnterprise1; //In the classes of the first enterprise
@Autowired MyBean mbForEnterprise2; //In the classes of the second enterprise

І це:

MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise1"); //In the classes of the first enterprise
MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise2"); //In the classes of the second enterprise

У будь-якому випадку вам доведеться щось змінити у вашій програмі, будь то класи чи файли конфігурації, але вам доведеться зробити це повторно.

Не було б непогано зробити щось подібне:

MyBean mb = (MyBean)MyFactory.get("mb"); 

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

Хіба це не буде корисніше, ніж писати дві конфігурації для кожного підприємства, а можливо, навіть мати два різних додатки для кожного ??

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

У будь-якому разі, розділ коментарів, якщо він відкритий, ви можете вбити мене.


3
Забагато розробників думає, що рамки Dependency Injection створені, щоб принести щось нове. Як ви добре пояснюєте, традиційні фабрики (найчастіше абстрактні фабрики) можуть грати таку ж роль інверсії залежності. Інверсія проти ін'єкції? Для мене єдиною «перевагою» фреймворку Dependency Injection є те, що нам не потрібно змінювати / перекомпілювати код (оскільки найчастіше його можна налаштувати з XML, як Spring), коли залежність додається / змінюється. Чому слід уникати перекомпіляції? Щоб уникнути деяких потенційних людських помилок під час зміни залежності / фабрики. Але з нашими чудовими IDE, рефакторинг грає добре :)
Mik378

6

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


4

Завдяки ін'єкції залежності клієнту не потрібно самостійно отримувати свої залежності, все це заздалегідь підготовлено.

На заводах хтось повинен зателефонувати тим, щоб доставити створені об'єкти до місця, де вони потрібні.

Різниця полягає в основному в цьому одному рядку, де виконується виклик фабрики та отримання сконструйованого об'єкта.

Але на заводах ви повинні написати цей 1 рядок скрізь, де вам потрібен такий об’єкт. Завдяки DI вам просто потрібно створити проводку (співвідношення між використанням та створеним об'єктом) один раз і просто покладатися на присутність об'єкта згодом. З іншого боку, DI часто вимагає трохи більше (скільки залежить від рамки) роботи на підготовчій стороні.


3

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

"Давно були королівства зі своїми владними органами управління, які контролювали та приймали рішення на основі власних письмових правил. Пізніше було сформовано великий уряд, який усунув усі ці маленькі органи управління, які мають один набір правил (конституції) і реалізуються через суди"

Керівними органами маленьких королівств є "Фабрики"

Великим урядом є "інжектор залежності".


1
Отже, КОЛИ один кодессідер попереднього маленького уряду тепер має бути великим? Якою мірою?
В'язень ЗЕРО

3

Ви можете ознайомитись із цим посиланням для порівняння двох (та інших) підходів на реальному прикладі.

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

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


3

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

Одного разу хтось сказав мені, що це те, як ти ним користуєшся, має значення!

Саме так. У 90% випадків ви можете отримати посилання на об'єкт, використовуючи або Factory, або DI, і зазвичай ви закінчуєте останні. В інших 10% випадків використання Factory є лише правильним способом . Ці випадки включають отримання об'єктів змінною під час виконання параметрів. Подобається це:

IWebClient client = factoryWithCache.GetWebClient(url: "stackoverflow.com",
        useCookies: false, connectionTimeout: 120);

У цьому випадку отримати clientвід DI неможливо (або, принаймні, потребує потворного вирішення). Отже, як загальне правило для прийняття рішення: якщо залежність може бути отримана без будь-яких обчислених параметрів часу виконання, тоді DI є кращим, в іншому випадку використовуйте Factory.


2

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

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

Перевірте це посилання: Інжекція залежності


2

Біной,

Я не думаю, що вам доведеться вибирати одне над іншим.

Дія переміщення залежного класу чи інтерфейсу до конструктора класу чи сеттера відповідає схемі DI. Об'єкт, який ви передаєте конструктору або набору, може бути реалізований за допомогою Factory.

Коли користуватися? Використовуйте візерунок або візерунки, які є у вашій школі для розробників. Що їм найбільше комфортно, і їх найлегше зрозуміти.


2

Я вважаю, що три важливі аспекти регулюють об'єкти та їх використання:
1. Моменталізація (класу разом із ініціалізацією, якщо така є).
2. Ін'єкція (створеного таким чином екземпляра) там, де це потрібно.
3. Управління життєвим циклом (створений таким чином екземпляр).

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

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

У той час як Заводи можуть бути придатними для невеликих застосувань, для великих підприємств було б краще обрати DI на заводах.


1

Мої думки:

Ін'єкція залежності: передайте колабораторам як параметри конструкторам. Dependency Injection Framework: загальна та налаштована фабрика для створення об'єктів, що передаються як параметри конструкторам.


1
Так, саме. Майже всі згадки про ін'єкцію залежності (DI) у цьому питанні неправильно використовують цей термін на відміну від цього визначення. Контейнер для ін'єкційних залежностей (DIC) - це найпоширеніший фреймворк, який виступає як загальна та налаштована фабрика для створення об'єктів.
CXJ

1

Рамка для ін'єкцій - це реалізація заводської моделі.

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

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

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


1

Шаблон дизайну фабрики

Заводський шаблон дизайну характеризується:

  • Інтерфейс
  • Заняття з реалізації
  • Завод

Ви можете спостерігати за кількома речами, коли допитуєтесь як нижче

  • Коли фабрика створить об'єкт для класів реалізації - час виконання або час компіляції?
  • Що робити, якщо ви хочете переключити реалізацію під час виконання? - Неможливо

Вони обробляються введенням залежності.

Ін'єкційна залежність

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

У DI контейнер створює необхідні екземпляри та "вводить" їх у об'єкт.

Тим самим виключається статична інстанція.

Приклад:

public class MyClass{

  MyInterface find= null;

  //Constructor- During the object instantiation

  public MyClass(MyInterface myInterface ) {

       find = myInterface ;
  }

  public void myMethod(){

       find.doSomething();

  }
}

1

З номіналу вони виглядають однаково

Дуже просто кажучи, Factory Factory, Creative Creative Pattern допомагає створити нам об’єкт - "Визначте інтерфейс для створення об'єкта". Якщо у нас є тип ключових значень пулу об'єктів (наприклад, словник), передаючи ключ Фабриці (я маю на увазі Простий заводський шаблон), ви можете вирішити Тип. Робота виконана! З іншого боку, схоже, що це робить те ж саме, що складається із системи введення залежностей (наприклад, карта структури, Ninject, Unity ... тощо).

Але ... "Не винаходити колесо"

З архітектурної точки зору його обов'язковий шар і "Не винаходити колесо".

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

Не змінюючи "GetType / Create" з заводу, найчастіше нам потрібні додаткові функції (можливість використовувати XML для визначення залежностей, глузування та тестування одиниць тощо). Оскільки ви посилалися на "Карта структури", перегляньте список функцій "Карта структури" . Це явно більше, ніж просто вирішити просте картографування об’єктів. Не винаходити колесо!

Якщо у вас є молоток, все виглядає як цвях

Залежно від ваших вимог та того, який тип програми ви будуєте, вам потрібно зробити вибір. Якщо у нього є лише кілька проектів (може бути один або два ..) і передбачає мало залежностей, можна вибрати простіший підхід. Це як би використовувати доступ до даних ADO .Net через використання Entity Framework для простого 1 або 2 виклику баз даних, де введення EF є надлишком у цьому сценарії.

Але для більшого проекту, або якщо ваш проект стає більше, я б дуже рекомендував мати шар DI з рамкою і створити місце для зміни рамки DI, яку ви використовуєте (Використовуйте фасад у головному додатку (веб-додаток, Web Api, робочий стіл) ..етк.).


1

На заводі ви можете згрупувати пов’язані інтерфейси, тому якщо передані параметри можна згрупувати на заводі, то це також гарне рішення для constructor overinjectionперегляду цього коду *):

public AddressModelFactory(IAddressAttributeService addressAttributeService,
        IAddressAttributeParser addressAttributeParser,
        ILocalizationService localizationService,
        IStateProvinceService stateProvinceService,
        IAddressAttributeFormatter addressAttributeFormatter)
    {
        this._addressAttributeService = addressAttributeService;
        this._addressAttributeParser = addressAttributeParser;
        this._localizationService = localizationService;
        this._stateProvinceService = stateProvinceService;
        this._addressAttributeFormatter = addressAttributeFormatter;
    }

Подивіться на конструктор, вам потрібно лише здати IAddressModelFactoryтуди, тому менше параметрів *):

 public CustomerController(IAddressModelFactory addressModelFactory,
        ICustomerModelFactory customerModelFactory,
        IAuthenticationService authenticationService,
        DateTimeSettings dateTimeSettings,
        TaxSettings taxSettings,
        ILocalizationService localizationService,
        IWorkContext workContext,
        IStoreContext storeContext,
        ICustomerService customerService,
        ICustomerAttributeParser customerAttributeParser,
        ICustomerAttributeService customerAttributeService,
        IGenericAttributeService genericAttributeService,
        ICustomerRegistrationService customerRegistrationService,
        ITaxService taxService,
        CustomerSettings customerSettings,
        AddressSettings addressSettings,...

Ви бачите в CustomerControllerбагатьох пройдених параметрах. Так, ви можете бачити це так, constructor overinjectionале так працює DI. І з цим немає нічого поганого CustomerController.

*) Код від nopCommerce.


1

Простіше кажучи, залежність впорскування та фабричний метод передбачає механізм "push vs pull" відповідно.

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

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

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


0

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

Ми використовували рамку Spring на Java для DI. Клас синглтон ( Parent) повинен був інстанціювати нові об'єкти іншого класу ( Child), а у них були складні колабораціоністи:

@Component
class Parent {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
    }

    void method(int p) {
        Child c = new Child(dep1, dep2, ..., depN, p);
        // ...
    }
}

У цьому прикладі Parentдоводиться отримувати DepXекземпляри лише для передачі їх Childконструктору. Проблеми з цим:

  1. Parentмає більше знань, Childніж слід
  2. Parent має більше співробітників, ніж слід
  3. Додавання залежності Childвключає зміниParent

Це коли я зрозумів, Factoryщо тут ідеально впишеться:

  1. Він приховує всі, крім справжніх параметрів Childкласу, як це бачимоParent
  2. Він інкапсулює знання про створення A Child, які можна централізувати в конфігурації DI.

Це спрощений Parentклас та ChildFactoryклас:

@Component
class Parent {
    // ...
    @Autowired
    Parent(ChildFactory childFactory) {
        this.childFactory = childFactory;
    }

    void method(int p) {
        Child c = childFactory.newChild(p);
        // ...
    }
}

@Component
class ChildFactory {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
        // ...
        this.depN = depN;
    }

    Child newChild(int p) {
        return new Child(dep1, dep2, ..., depN, p);
    }
}

0

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


-1

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

Я використовую Factory, щоб створити свої різні об'єкти шару (Бізнес, Доступ до даних).

ICarBusiness carBusiness = BusinessFactory.CreateCarBusiness();

Інший розробник побачить це, і при створенні об'єкта Business Layer він виглядає в BusinessFactory, а Intellisense надає розробнику всі можливі бізнес-шари для створення. Не потрібно грати в гру, знайдіть інтерфейс, який я хочу створити.

Ця структура вже є Інверсією управління. Я більше не відповідальний за створення конкретного об’єкта. Але вам все одно потрібно забезпечити введення залежності, щоб мати можливість легко змінити речі. Створення власної індивідуальної ін'єкції залежності залежить від сміху, тому я використовую Unity. У межах CreateCarBusiness () я прошу Unity вирішити, який клас належить до цього, і це все життя.

Отже, мій код Структура впорскування заводської залежності:

public static class BusinessFactory
{
    public static ICarBusiness CreateCarBusiness()
    {
       return Container.Resolve<ICarBusiness>();
    }
}

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

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


-4

На мою думку, використання ін'єкцій залежностей набагато краще, якщо ви: 1. розгортаєте свій код у невеликому розділі, оскільки він добре справляється з роз'єднанням одного великого коду. 2. Тестабільність - один із випадків, коли DI є нормальним у використанні, оскільки ви можете легко знущатися над нерозв’язаними об'єктами. за допомогою інтерфейсів ви можете легко знущатися та перевіряти кожен об’єкт. 3. Ви можете одночасно переглядати кожну частину програми, не потребуючи кодування іншої її частини з моменту її слабкого відключення.

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