65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (5投票s)

2021年2月27日

CPOL

4分钟阅读

viewsIcon

16800

downloadIcon

239

一篇简单的文章,用于理解使用 C# 进行依赖注入的概念

引言

依赖注入是大多数开发人员最初难以掌握的概念。老实说,这有点令人望而生畏。我花了很多时间才真正理解它。在这篇文章中,我将分解这个概念,以帮助我的同行开发人员理解 DI 以及我们为什么需要它。

关注点

  1. 什么是依赖注入?
  2. 我们为什么需要 DI?它解决了什么问题?
  3. 我们如何实现它?

我可以继续问更多问题,但我不会,因为我不想把事情搞复杂。让我们以最简单的方式清楚地学习。

示例项目:初始步骤

为了理解问题,让我们编写一个不使用依赖注入的简单控制台应用程序,并遵守一条规则。

规则应用程序代码必须是可测试的。(可测试 = 代码必须支持单元测试实现)

步骤 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 类。

要求

  1. OrderProcessor 应该接收一个 Order 对象作为输入并进行处理。
  2. OrderProcessor 应该使用 ValidateOrder 类来验证 order 详细信息。
  3. OrderProcessor 应该具备使用 PrintOrder 类打印已处理订单详细信息的能力。

引入新类:ValidateOrderPrintOrder

  • 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

OrderProcessorProcessOrder 方法使用了 validateOrderprintOrder 对象。

依赖注入是一个概念,它帮助我们将紧耦合的代码转换为松耦合的代码,从而使我们的解决方案从不可测试变为可测试。

到目前为止,我们已经成功实现了一个可以操作的示例项目。

在第二部分中,将详细解释 DI 帮助我们解决的确切问题。敬请期待。

在这篇文章的顶部,我附上了本文完整的源代码文件供大家参考。相同的代码将在下一篇文章中更新,所以请注意,这并不是本文代码的最终版本。

感谢阅读。永不停止学习!

历史

  • 2021年2月27日:初次发布
© . All rights reserved.