Инжектирането на зависимост (DI) разчита ли на интерфейси?

Това може да изглежда очевидно за повечето хора, но аз просто се опитвам да потвърдя, че инжектирането на зависимост (DI) разчита на използването на интерфейси.

По-конкретно, в случай на клас, който има определен интерфейс като параметър в своя конструктор или определен интерфейс, дефиниран като свойство (известен още като Setter), DI рамката може да предаде екземпляр на конкретен клас, за да задоволи нуждите на този интерфейс в този клас. (Извинявам се, ако това описание не е ясно. Имам проблем да го опиша правилно, защото терминологията/концепциите все още са малко нови за мен.)

Причината да попитам е, че в момента имам клас, който има някаква зависимост. Не толкова зависимост от обект, а URL. Класът изглежда така [C#]:

using System.Web.Services.Protocols;
public partial class SomeLibraryService : SoapHttpClientProtocol 
{
        public SomeLibraryService() 
        {
            this.Url = "http://MyDomainName.com:8080/library-service/jse";
        }
}

Класът SoapHttpClientProtocol има публично свойство, наречено Url (което е обикновен стар „низ“) и конструкторът тук го инициализира до твърдо кодирана стойност.

Мога ли да използвам DI рамка, за да инжектирам различна стойност при изграждането? Мисля, че не, тъй като this.Url не е нещо като Interface; това е String.

[Между другото, кодът по-горе беше „автоматично генериран от wsdl“, според коментарите в кода, с който работя. Така че не искам особено да променям този код, въпреки че не виждам себе си да го генерирам отново. Така че може би промяната на този код е добре.]

Мога да се видя как правя алтернативен конструктор, който приема низ като параметър и инициализира this.Url по този начин, но не съм сигурен, че това е правилният подход по отношение на поддържането на слабо свързано разделяне на проблемите. (SoC)

Някакви съвети за тази ситуация?


person Pretzel    schedule 15.07.2010    source източник


Отговори (4)


Не е необходимо да използвате интерфейси - можете да използвате конкретни типове или абстрактни базови класове. Но много от предимствата на DI (като възможността да промените изпълнение на зависимост) идват при използване на интерфейси.

Castle Windsor (рамката на DI, която познавам най-добре), ви позволява да картографирате обекти в IoC контейнера към интерфейси или само към имена, което ще работи във вашия случай.

person James Curran    schedule 15.07.2010
comment
Давам ви зелената отметка, но отговорите на Дейвид и Питър също бяха много добри. Благодаря за пояснението, че не се изискват интерфейси. - person Pretzel; 15.07.2010
comment
... идват, когато използвате интерфейси, това е неправилно или подвеждащо. Мисля, че или премахнете това, или го архивирайте с това как използването на абстрактен базов клас би ме попречило да променя изпълнението на зависимост. - person Luke Puplett; 12.04.2019
comment
@LukePuplett: Ако сте използвали подигравателна рамка за изграждане на обекти за тестване на единици (една от основните обосновки за DI), ще трябва да използвате интерфейс. - person James Curran; 17.04.2019
comment
@JamesCurran Не, подобно на ръчното подкласиране за полиморфизъм, подигравката работи в рамките на същите ограничения; членовете трябва да са абстрактни или виртуални. Както Дейвид извиква, DI е актът на предоставяне на зависимостите на класа или системата, която ще се използва, дори и ръчно, без автоматизиран DI контейнер като Unity. Относно вашия въпрос, аз не използвам подигравателни библиотеки. Пиша наистина добри фалшификати и тествам само поведението на публичния API, оставяйки ми свободата да преработвам често и лесно. Подигравката замъглява тестовете и свързва тестовете с изпълнението. Да се ​​налага да се подигравате със собствения си код е знак за дефект в дизайна. - person Luke Puplett; 24.04.2019

DI наистина просто означава, че класът няма да конструира своите външни зависимости и няма да управлява живота на тези зависимости. Зависимостите могат да бъдат инжектирани или чрез конструктор, или чрез параметър на метода. Интерфейсите или абстрактните типове са често срещани за изясняване на договора, който потребителят очаква от неговата зависимост, но в някои случаи могат да бъдат инжектирани и прости типове.

Например, клас в библиотека може да извика HttpContext.Current вътрешно, което прави произволни предположения за приложението, в което ще се хоства кодът. DI версия на библиотечния метод би очаквала инстанция на HttpContext да бъде инжектирана чрез параметър и т.н.

person David    schedule 15.07.2010

Инжектирането на зависимости е начин за организиране на вашия код. Може би част от вашето объркване идва от факта, че няма един официален начин да го направите. Може да се постигне с помощта на "обикновен" c# код или с помощта на рамка като Castle Windsor. Понякога (често?) това включва използване на интерфейси. Без значение как се постига, основната цел на DI обикновено е да направи вашия код по-лесен за тестване и по-лесен за модифициране по-късно.

Ако трябваше да инжектирате URL адреса във вашия пример чрез конструктор, това може да се счита за „ръчен“ DI. В статията за DI в Уикипедия има още примери за ръчно спрямо рамково DI.

person Peter Recore    schedule 15.07.2010
comment
Аз съм съгласен да. Част от объркването идва от това, че няма стандартизиран начин за това. DI ми беше представен чрез книгата Pro ASP.net MVC Framework (от Sanderson) и всеки показан пример използва интерфейси, така че заключението ми беше, че работи само върху интерфейси, но логично нямаше смисъл в главата ми, така че Трябваше да попитам. Сандерсън говори за замъка Уиндзор в първата си книга, но сега използва Ninject във второто си издание (което току-що излезе от печатарските преси миналата седмица.) И да, започвам да осъзнавам, че DI помага много при Unit Testing. - person Pretzel; 15.07.2010

Бих искал да отговоря с акцент върху използването на интерфейси в .NET приложения. Полиморфизмът в .NET може да бъде постигнат чрез виртуални или абстрактни методи или интерфейси.

Във всички случаи има сигнатура на метод без никаква реализация или реализация, която може да бъде заменена.

„Договорът“ на функция (или дори свойство) е дефиниран, но как се изпълнява методът, логическите вътрешности на метода могат да бъдат различни по време на изпълнение, определени от това кой подклас се инстанцира и предава на метода или конструктора, или задайте върху свойство (актът на „инжектиране“).

Официалните насоки за проектиране на тип .NET препоръчват използването на абстрактни базови класове над интерфейси, тъй като те имат по-добри възможности за развитието им след изпращане, могат да включват претоварвания за удобство и са по-способни да се самодокументират и да съобщават правилната употреба на изпълнителите.

Трябва обаче да се внимава да не се добавя логика. Изкушението да го направят е изгаряло хората в миналото, толкова много хора използват интерфейси - много други хора използват интерфейси, просто защото това е, което програмистите, които седят около тях, правят.

Също така е интересно да се отбележи, че докато самият DI рядко се използва прекалено много, използването на рамка за извършване на инжектирането доста често се използва прекалено много в ущърб на повишената сложност, верижна реакция може да се осъществи, когато са необходими повече и повече типове в контейнера, въпреки че никога не са „превключвани“.

IoC рамките трябва да се използват пестеливо, обикновено само когато трябва да разменяте обекти по време на изпълнение, според средата или конфигурацията. Това обикновено означава превключване на "шевовете" на основните компоненти в приложението, като например обектите на хранилището, използвани за абстрахиране на вашия слой данни.

За мен истинската сила на една IoC рамка е да превключва изпълнението на места, където нямате контрол върху създаването. Например в ASP.NET MVC създаването на класа на контролера се извършва от ASP.NET framework, така че инжектирането на каквото и да било е невъзможно. Рамката ASP.NET има някои кукички, които IoC рамките могат да използват, за да „влязат между“ процеса на създаване и да изпълнят своята магия.

Лука

person Luke Puplett    schedule 15.11.2013
comment
Вижте Промените за прекъсване на двоичните файлове в тази статия. docs.microsoft.com/en-us/dotnet/ standard/library-guidance/, който препоръчва използването на абстрактни базови класове над интерфейси. - person Luke Puplett; 12.04.2019