使用 C# 中的简单实时应用程序进行依赖注入(第 I 部分)






4.56/5 (5投票s)
一篇简单的文章,用于理解使用 C# 进行依赖注入的概念
引言
依赖注入是大多数开发人员最初难以掌握的概念。老实说,这有点令人望而生畏。我花了很多时间才真正理解它。在这篇文章中,我将分解这个概念,以帮助我的同行开发人员理解 DI 以及我们为什么需要它。
关注点
- 什么是依赖注入?
- 我们为什么需要 DI?它解决了什么问题?
- 我们如何实现它?
我可以继续问更多问题,但我不会,因为我不想把事情搞复杂。让我们以最简单的方式清楚地学习。
示例项目:初始步骤
为了理解问题,让我们编写一个不使用依赖注入的简单控制台应用程序,并遵守一条规则。
规则:应用程序代码必须是可测试的。(可测试 = 代码必须支持单元测试实现)
步骤 1:打开 Visual Studio。
步骤 2:创建一个新的控制台项目。将项目命名为 DependencyInjection
。
步骤 3:向项目中添加一个新的代码文件。将文件命名为 OrderProcessor.cs。
考虑 OrderProcessor
类,它通过将 Orders
作为参数来提供处理 Orders
的功能。
到目前为止,很简单。
(将其想象成亚马逊订单管理系统非常核心级别的实现。我们大多数人在亚马逊购物平台上都下过订单。)
为了有一个 OrderProcessor
机制,我们首先需要有一个 Order
。所以,我们现在需要两个类。
要实现的类
Order
:一个具有订单属性的类。换句话说,它是一个模型。(模型 = 仅包含属性的类,通常用于表示数据库中的表)。OrderProcessor
:一个具有Order
对象处理机制的类。
//Order.cs
namespace DependencyInjection
{
public class Order
{
public int Id {get;set;}
public int CustomerId {get; set;}
public decimal TotalAmount {get;set;}
public int Status {get;set;}
public List<OrderDetail> Details {get;set;}
}
}
等等,OrderDetail
是什么?引入新类:OrderDetail
。
OrderDetail
:一个类,其中包含该订单中的产品信息。
现在,Products
也是独立的对象,所以我们也需要为 Product
创建一个模型。
Product
:一个具有常规产品属性的类。(例如,Pen
就是一个产品)。
//OrderDetails.cs
using System.Collections.Generic;
namespace DependencyInjection
{
public class OrderDetail
{
public int OrderDetailId {get;set;}
public int OrderId {get;set;}
public Product Item{get;set;}
public int Quantity {get;set;}
}
}
//Product.cs
namespace DependencyInjection
{
public class Product
{
public int Id{get;set;}
public string ProductName {get; set;}
public decimal Price {get;set;}
}
}
现在,我们的基本模型已经准备好了。让我们来描绘一下。
步骤 4:在项目中创建一个名为 Models 的文件夹,并将上述设计的模型放在该文件夹内。
步骤 5:让我们实现 OrderProcessor
类。
要求
OrderProcessor
应该接收一个Order
对象作为输入并进行处理。OrderProcessor
应该使用ValidateOrder
类来验证order
详细信息。OrderProcessor
应该具备使用PrintOrder
类打印已处理订单详细信息的能力。
引入新类:ValidateOrder
、PrintOrder
ValidateOrder
:一个类,具有验证订单的机制,并将Order
对象作为输入。PrintOrder
:一个类,能够打印给定Order
对象的详细信息。
//OrderProcessor.cs
using System;
namespace DependencyInjection
{
public class OrderProcessor
{
Order order;
public OrderProcessor(Order order)
{
this.order = order;
}
public void ProcessOrder()
{
Console.WriteLine("Process Started.");
ValidateOrder validateOrder = new ValidateOrder();
if (validateOrder.CheckAllDetails(order))
{
//Processing order.
foreach (var orderDetail in order.Details)
{
order.TotalAmount += orderDetail.Item.Price *
orderDetail.Quantity;
}
PrintOrder printOrder = new PrintOrder();
printOrder.PrintAllDetails(order);
Console.WriteLine($"Order : {order.Id} has been processed.");
}
else
{
Console.WriteLine($"Order : {order.Id} processing failed.");
}
Console.WriteLine("Process Ended.");
}
}
}
//ValidateOrder.cs
namespace DependencyInjection
{
public class ValidateOrder
{
public bool CheckAllDetails(Order order)
{
return order.Id > 0;
}
}
}
//PrintOrder.cs
using System;
namespace DependencyInjection
{
public class PrintOrder
{
public void PrintAllDetails(Order order)
{
Console.WriteLine($"OrderID : {order.Id}");
Console.WriteLine($"CustomerID : {order.CustomerId}");
foreach(var orderDetail in order.Details)
{
Console.WriteLine($"Product Name : {orderDetail.Item.ProductName}
(Price : {orderDetail.Item.Price})(Quantity : { orderDetail.Quantity })");
}
Console.WriteLine($"TotalAmount : {order.TotalAmount}");
}
}
}
让我们来描绘一下我们的 worker
类。
主程序
让我们看看我们的 Main
程序。
//Program.cs
using System;
using System.Collections.Generic;
namespace DependencyInjection
{
class Program
{
static void Main(string[] args)
{
//Products
var productPen = new Product()
{ Id = 1, ProductName = "Pen", Price = 10 };
var productNotebook = new Product()
{ Id = 2, ProductName = "Notebook", Price = 35 };
//OrderDetail
List<OrderDetail> orderDetails = new List<OrderDetail>();
orderDetails.Add(new OrderDetail()
{
OrderDetailId = 1,
OrderId = 1,
Item = productPen,
Quantity = 1
});
orderDetails.Add(new OrderDetail()
{
OrderDetailId = 2,
OrderId = 1,
Item = productNotebook,
Quantity = 1
});
//Order
Order order = new Order();
order.Id = 1;
order.Details = orderDetails;
order.CustomerId = 1001;
OrderProcessor orderProcessor = new OrderProcessor(order);
orderProcessor.ProcessOrder();
}
}
}
简单来说,在我们的主程序中,创建了一个 OrderDetail
对象列表,并将其赋予一个 Order
对象。
通过将带有详细信息的 order
传递给 OrderProcessor
的构造函数,我们正在处理 order
。
这个简单应用程序的输出如下所示。
我们完成了吗?还没有。请记住规则。
规则:应用程序代码必须是可测试的。(可测试 = 代码必须支持单元测试实现。)
我们已经创建了一个符合要求的应用程序。但是为了确认我们的代码质量并确保它在不同场景下不会出错,我们需要编写 UnitTest
。
市面上有工具可以应用测试项目到我们的解决方案中,但有一个问题。要应用 UnitTest
,目标代码必须是可测试的。
我们的代码是可测试的吗?不是。
为什么它不是可测试的代码?因为 OrderProcessor
类包含紧耦合的代码。
什么是紧耦合代码?简单来说,直接在代码中实例化其依赖类的代码就是紧耦合的。
我们说 OrderProcessor
包含紧耦合的代码,是因为它包含以下代码块。
ValidateOrder validateOrder = new ValidateOrder(); //Dependency
PrintOrder printOrder = new PrintOrder(); //Dependency
OrderProcessor
的 ProcessOrder
方法使用了 validateOrder
和 printOrder
对象。
依赖注入是一个概念,它帮助我们将紧耦合的代码转换为松耦合的代码,从而使我们的解决方案从不可测试变为可测试。
到目前为止,我们已经成功实现了一个可以操作的示例项目。
在第二部分中,将详细解释 DI 帮助我们解决的确切问题。敬请期待。
在这篇文章的顶部,我附上了本文完整的源代码文件供大家参考。相同的代码将在下一篇文章中更新,所以请注意,这并不是本文代码的最终版本。
感谢阅读。永不停止学习!
历史
- 2021年2月27日:初次发布