Як використовувати ін'єкцію залежності в поєднанні з заводською схемою


10

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

Розглянемо клас B, який потребує вмісту файлу product.xml. Цей клас повинен буде інстанціювати відповідний конкретний реалізатор інтерфейсу Parser для розбору файлу XML. Я можу делегувати інстанціювання відповідного конкретного реалізатора на завод, такий, що клас B "має-а". Однак клас B буде "залежати" від Factory за допомогою екземпляра конкретного реалізатора. Це означає, що конструктор або метод встановлення класу B потрібно буде передати Фабриці.

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

Відповіді:


8

Правильний спосіб зробити це - залежати від інтерфейсу, а потім ввести реалізацію цього інтерфейсу в клас B.

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

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

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

Підсумок: не соромтеся вводити залежності. Це повинен бути нормальний спосіб робити речі.


Чи впорскування конструктора відкладає нові речі якомога довше?
JeffO

Це було жорстоко - я виправдав це, щоб сказати "якнайдалі у вашій програмі".
Нік Ходжес

Так. Я думав залежно від інтерфейсу Parser замість фабрики. Тепер, якщо інший клас використовує клас B, він повинен буде залежати від інтерфейсу, від якого залежить клас B. Це дозволить продовжувати весь шлях до класу вищого рівня. Цей клас вищого рівня повинен буде нарешті використати завод, щоб створити відповідний конкретний екземпляр Парсера. Чи повинен клас вищого рівня залежати від фабрики чи він повинен використовувати фабрику безпосередньо всередині своїх методів?
CKing

1
Добре сказано: Не
соромтеся

9

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

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

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

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

тобто

IDbConnectionProvider connProvider = DIContainer.Get<IDbConnectionProvider>();
IUserRepository userRepo = new UserRepository(connProvider);
User currentUser = userRepo.GetCurrentUser();

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

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

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


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

Я думаю, ви пропустили суть, оскільки ОП не говорила про рамки DI чи про контейнери DI.
Doc Brown

@Jimmy Hoffa Хоча ви робили дуже цікаві моменти, я знаю про контейнери IoC, такі як Spring та cconcept Dependency Injection. Моє занепокоєння стосувалося сценарію, коли ви не використовуєте IoC-фреймворк, в такому випадку вам потрібно написати клас, який буде інстанціювати ваші залежності для вас. Якщо клас A залежить від інтерфейсу B, а клас C використовує клас A, то клас C залежить від інтерфейсу B. Хтось повинен дати класу C класу B ce. Якщо клас C тоді залежатиме від класу
CKing

Якщо клас A залежить від інтерфейсу B, а клас C використовує клас A, то клас C залежить від інтерфейсу B. Хтось повинен надати класу C інтерфейс. залежності, тобто фабрика. Ви, здається, відповіли на це питання у своїй відповіді, але з інформаційним перенавантаженням :)
CKing

@bot, як я вже говорив у передмові, значною мірою ви можете ставитися до заводів як до підмножини функціоналу контейнера DI, це неправда у всіх сценаріях, як згадував Док Браун, але подивіться на мій зразок коду та замініть DIContainerна DbConnectionFactoryі концепцію все ще стоїть на увазі, що ви отримаєте конкретну реалізацію від вашого DI / Factory / тощо та передаєте її споживачам типу на час їх виготовлення.
Джиммі Хоффа

5

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

Я використовую DI для ін'єкцій на заводах досить регулярно.


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

4

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


class Parent
{
    private IParserFactory parserFactory;
    public Parent(IParserFactory factory)
    {
        parserFactory = factory
    }

    public ParsedObject ParseFrom(string filename, FileType fileType)
    {
        var parser = parserFactory.CreateFor(fileType);
        return parser.Parse(filename);
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.