JavaRush /Java 博客 /Random-ZH /注入依赖项的简单方法

注入依赖项的简单方法

已在 Random-ZH 群组中发布
依赖注入(DI)并不是一个容易理解的概念,将其应用到新的或现有的应用程序中更是令人困惑。Jess Smith向您展示了如何使用 C# 和 Java 编程语言在没有注入容器的情况下进行依赖项注入。 依赖注入的简单方法 - 1在本文中,我将向您展示如何在 .NET 和 Java 应用程序中实现依赖项注入 (DI)。依赖注入的概念在 2000 年首次引起开发人员的注意,当时 Robert Martin 撰写了文章“设计原则和模式”(后来以缩写SOLID为人所知)。SOLID 中的 D 指的是反转依赖(DOI),后来被称为依赖注入。最初也是最常见的定义:依赖倒置是基类管理依赖关系的方式的倒转。Martin 的原始文章使用以下代码来说明类Copy对较低级别类的依赖关系WritePrinter
void Copy()
	{
	 int c;
	 while ((c = ReadKeyboard()) != EOF)
		WritePrinter(c);
	}
第一个明显的问题是,如果更改方法的参数列表或类型WritePrinter,则需要在依赖该方法的任何地方实现更新。此过程增加了维护成本,并且是新错误的潜在来源。
有兴趣阅读有关 Java 的内容吗?加入Java 开发者小组!
另一个问题:Copy 类不再是重用的潜在候选者。例如,如果您需要将从键盘输入的字符输出到文件而不是打印机,该怎么办?为此,您可以Copy按如下方式修改该类(C++ 语言语法):
void Copy(outputDevice dev)
	{
	int c;
	while ((c = ReadKeyboard()) != EOF)
		if (dev == printer)
			WritePrinter(c);
		else
			WriteDisk(c);
	}
尽管引入了新的依赖项WriteDisk,情况并没有改善(反而变得更糟),因为违反了另一个原则:“软件实体,即类、模块、函数等,应该对扩展开放,但对扩展关闭”。修改。” Martin 解释说,这些新的条件 if/else 语句降低了代码的稳定性和灵活性。解决方案是反转依赖关系,使写入和读取方法依赖于Copy. 它们不是“推送”依赖项,而是通过构造函数传递。修改后的代码如下所示:
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);
	}
现在, 该类Copy可以通过类方法的不同实现轻松地重用ReaderWriter该类Copy没有有关类型Reader和的内部结构的任何信息Writer,因此可以在不同的实现中重用它们。但如果这一切对您来说似乎是某种官样文章,也许下面的 Java 和 C# 示例会澄清这种情况。

Java 和 C# 示例

为了说明无需依赖项容器即可轻松进行依赖项注入,我们从一个简单的示例开始,DI只需几个步骤即可自定义该示例以供使用。假设我们有一个类HtmlUserPresentation,当调用它的方法时,会生成一个 HTML 用户界面。这是一个简单的例子:
HtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
任何使用此类代码的项目都会对该类产生依赖HtmlUserPresentation,从而导致上述可用性和可维护性问题。一项改进立即显现出来:创建一个带有类中当前可用的所有方法的签名的接口HtmlUserPresentation。以下是该接口的示例:
public interface IHtmlUserPresentation {
	String createTable(ArrayList rowVals, String caption);
	String createTableRow(String tableCol);
	// Оставшиеся сигнатуры
}
创建接口后,我们修改类HtmlUserPresentation以使用它。返回到实例化 type HtmlUserPresentation,我们现在可以使用接口类型而不是基本类型:
IHtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
创建接口使我们能够轻松使用IHtmlUserPresentation. 例如,如果我们想测试这种类型,我们可以轻松地将基本类型替换HtmlUserPresentation为另一种名为 的类型HtmlUserPresentationTest。到目前为止所做的更改使代码更易于测试、维护和扩展,但对重用没有任何作用,因为HtmlUserPresentation使用该类型的所有类仍然知道它的存在。要删除这种直接依赖关系,您可以将接口类型传递IHtmlUserPresentation给将使用它的类或方法的构造函数(或方法参数列表):
public UploadFile(IHtmlUserPresentation htmlUserPresentation)
构造函数UploadFile现在可以访问该类型的所有功能IHtmlUserPresentation,但对实现该接口的类的内部结构一无所知。在此上下文中,类型注入在创建类的实例时发生UploadFileIHtmlUserPresentation通过将不同的实现传递给需要不同功能的不同类或方法, 接口类型变得可重用。

结论和巩固材料的建议

您了解了依赖注入,并且当其中一个类实例化另一个类以访问目标类型的功能时,类被称为直接相互依赖。要解耦两种类型之间的直接依赖关系,您应该创建一个接口。接口使类型能够根据所需功能的上下文包含不同的实现。通过将接口类型传递给类构造函数或方法,需要该功能的类/方法不知道有关实现该接口的类型的任何详细信息。因此,接口类型可以在需要相似但不相同行为的不同类之间重用。
  • 要试验依赖项注入,请查看一个或多个应用程序中的代码,并尝试将频繁使用的基本类型转换为接口。

  • 更改直接实例化此基类型的类以使用此新接口类型,并将其传递到将使用它的类方法的构造函数或参数列表。

  • 创建一个测试实现来测试此接口类型。一旦你的代码被重构,DI它将变得更容易实现,并且你会注意到你的应用程序在重用性和可维护性方面变得更加灵活。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION