JavaRush /Java Blog /Random-TL /Isang madaling paraan upang mag-inject ng mga dependency

Isang madaling paraan upang mag-inject ng mga dependency

Nai-publish sa grupo
Ang dependency injection (DI) ay hindi isang madaling konsepto na maunawaan, at ang paglalapat nito sa bago o umiiral na mga application ay mas nakakalito. Ipinapakita sa iyo ni Jess Smith kung paano gumawa ng dependency injection nang walang lalagyan ng injection sa C# at Java programming language. Простой способ внедрения зависимостей - 1Sa artikulong ito, ipapakita ko sa iyo kung paano ipatupad ang dependency injection (DI) sa .NET at Java na mga application. Ang konsepto ng dependency injection ay unang dumating sa atensyon ng mga developer noong 2000, nang isulat ni Robert Martin ang artikulong "Design Principles and Patterns" (na kalaunan ay kilala sa acronym na SOLID ). Ang D sa SOLID ay tumutukoy sa Dependency of Inversion (DOI), na kalaunan ay naging kilala bilang dependency injection. Ang orihinal at pinakakaraniwang kahulugan: ang dependency inversion ay isang inversion ng paraan ng pamamahala ng base class sa mga dependencies. Ginamit ng orihinal na artikulo ni Martin ang sumusunod na code upang ilarawan ang dependency ng isang klase Copysa isang mas mababang antas ng klase WritePrinter:
void Copy()
	{
	 int c;
	 while ((c = ReadKeyboard()) != EOF)
		WritePrinter(c);
	}
Ang unang halatang problema ay kung babaguhin mo ang listahan ng parameter o mga uri ng isang paraan WritePrinter, kailangan mong ipatupad ang mga update kung saan man mayroong dependency sa pamamaraang iyon. Ang prosesong ito ay nagpapataas ng mga gastos sa pagpapanatili at isang potensyal na mapagkukunan ng mga bagong error.
Interesado sa pagbabasa tungkol sa Java? Sumali sa Java Developer group !
Isa pang problema: ang klase ng Kopyahin ay hindi na isang potensyal na kandidato para sa muling paggamit. Halimbawa, paano kung kailangan mong mag-output ng mga character na ipinasok mula sa keyboard patungo sa isang file sa halip na sa isang printer? Upang gawin ito, maaari mong baguhin ang klase Copytulad ng sumusunod (C++ language syntax):
void Copy(outputDevice dev)
	{
	int c;
	while ((c = ReadKeyboard()) != EOF)
		if (dev == printer)
			WritePrinter(c);
		else
			WriteDisk(c);
	}
Sa kabila ng pagpapakilala ng isang bagong dependency WriteDisk, ang sitwasyon ay hindi bumuti (sa halip ay lumala) dahil ang isa pang prinsipyo ay nilabag: "mga entity ng software, iyon ay, mga klase, module, function, at iba pa, ay dapat na bukas para sa extension, ngunit sarado para sa pagbabago." Ipinaliwanag ni Martin na ang mga bagong conditional if/else na mga pahayag na ito ay nagbabawas sa katatagan at flexibility ng code. Ang solusyon ay ang baligtarin ang mga dependency upang ang mga paraan ng pagsulat at pagbasa ay nakasalalay sa Copy. Sa halip na "popping" dependencies, ipinapasa ang mga ito sa constructor. Ang binagong code ay ganito ang hitsura:
class Reader
	{
		public:
		virtual int Read() = 0;
	};
	class Writer
	{
		public:
		virtual void Write(char) = 0;
	};
	void Copy(Reader& r, Writer& w)
	{
		int c;
		while((c=r.Read()) != EOF)
		w.Write(c);
	}
Ngayon ang klase Copyay madaling magamit muli sa iba't ibang pagpapatupad ng mga pamamaraan ng klase Readerat Writer. Ang klase Copyay walang anumang impormasyon tungkol sa panloob na istraktura ng mga uri Readerat Writer, na ginagawang posible na muling gamitin ang mga ito sa iba't ibang pagpapatupad. Ngunit kung ang lahat ng ito ay tila isang uri ng gobbledygook sa iyo, marahil ang mga sumusunod na halimbawa sa Java at C# ay linawin ang sitwasyon.

Halimbawa sa Java at C#

Для иллюстрации простоты внедрения зависимостей без контейнера зависимостей, начнем с простого примера, который можно переделать под использование DI всего за несколько шагов. Допустим, у нас есть класс HtmlUserPresentation, который, при вызове его методов, формирует пользовательский HTML-интерфейс. Вот простой пример:
HtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
У любого использующего этот code класса проекта появляется зависимость от класса HtmlUserPresentation, что приводит к вышеописанным проблемам с удобством использования и обслуживанием. Сразу напрашивается усовершенствование: создание интерфейса с сигнатурами всех ныне имеющихся в классе HtmlUserPresentation методов. Вот пример этого интерфейса:
public interface IHtmlUserPresentation {
	String createTable(ArrayList rowVals, String caption);
	String createTableRow(String tableCol);
	// Оставшиеся сигнатуры
}
После создания интерфейса, модифицируем класс HtmlUserPresentation для его использования. Возвращаясь к созданию экземпляра типа HtmlUserPresentation, мы можем теперь использовать тип интерфейса instead of базового:
IHtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
Creation интерфейса позволяет нам легко использовать другие реализации типа IHtmlUserPresentation. Например, если мы хотим протестировать этот тип, то легко можем заменить базовый тип HtmlUserPresentation на другой тип, под названием HtmlUserPresentationTest. Выполненные до сих пор изменения упрощают тестирование, обслуживание и масштабирование codeа, но ничего не делают для переиспользования, поскольку все использующие тип HtmlUserPresentation классы все еще знают о его существовании. Whatбы убрать эту прямую зависимость, можно передавать интерфейсный тип IHtmlUserPresentation в конструктор (or список параметров метода) класса or метод, который его будет использовать:
public UploadFile(IHtmlUserPresentation htmlUserPresentation)
У конструктора UploadFile теперь есть доступ ко всей функциональности типа IHtmlUserPresentation, но он ничего не знает о внутреннем устройстве реализующего этот интерфейс класса. В данном контексте, внедрение типа происходит при создании экземпляра класса UploadFile. Интерфейсный тип IHtmlUserPresentation становится переиспользуемым, передавая различные реализации различным классам or методам, для которых необходима разная функциональность.

Заключение и рекомендации для закрепления материала

Вы узнали о том, что такое внедрение зависимостей и о том, что классы называются напрямую зависящими друг от друга тогда, когда один из них создает экземпляр другого для получения доступа к функциональности целевого типа. Для расцепления прямой зависимости между двумя типами следует создать интерфейс. Интерфейс предоставляет типу возможность включать различные реализации, в зависимости от контекста необходимой функциональности. Благодаря передаче интерфейсного типа конструктору or методу класса, класс/метод, для которого нужна функциональность, не знает ниHowих подробностей о реализующем интерфейс типе. В силу этого интерфейсный тип можно использовать повторно для различных классов, требующих схожего, но не одинакового поведения.
  • Whatбы поэкспериментировать с внедрением зависимостей, просмотрите свой code из одного or нескольких приложений и попробуйте переделать интенсивно используемый базовый тип в интерфейс.

  • Измените непосредственно создающие экземпляры этого базового типа классы так, чтобы они использовали этот новый интерфейсный тип и передавали его через конструктор or список параметров метода класса, который его должен будет использовать.

  • Создайте тестовую реализацию для проверки этого интерфейсного типа. После рефакторинга вашего codeа реализовать DI станет проще, и вы заметите, насколько более гибким станет ваше приложение в смысле переиспользования и сопровождения.
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION