依赖注入基础
本课属于 Coddy C# 学习旅程的“面向对象编程”单元。第 46 课,共 70 课。
依赖注入(DI)是一种技术,类通过外部获取依赖项,而不是自己创建它们。这样可以让代码更灵活、更易于测试,并降低耦合度。
不使用 DI 时,类会自行创建依赖项,因此很难更改或测试:
public class OrderService
{
private EmailSender sender = new EmailSender(); // 紧密耦合
public void PlaceOrder() => sender.Send("Order placed");
}使用 DI 时,依赖项会通过构造函数传入:
public interface IMessageSender
{
void Send(string message);
}
public class EmailSender : IMessageSender
{
public void Send(string message) => Console.WriteLine("Email: " + message);
}
public class OrderService
{
private IMessageSender sender;
public OrderService(IMessageSender sender)
{
this.sender = sender;
}
public void PlaceOrder() => sender.Send("Order placed");
}现在,OrderService 不知道也不关心它使用的是电子邮件、短信,还是用于测试的模拟发送器。创建对象时,会“注入”这个依赖项:
IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder(); // 邮件:订单已提交这种模式遵循依赖抽象(接口)而非具体实现的原则。这样一来,你可以替换实现,而无需修改使用它的类。这对于单元测试和维护灵活的架构至关重要。
挑战
简单让我们构建一个日志系统,展示依赖注入的强大之处。你将创建一个灵活的 Logger 类,而不是硬编码日志的写入方式。这个类可以与任何日志目标配合使用,例如控制台、模拟文件或其他目标,而不需要了解具体细节。
你将把代码分到三个文件中:
ILogWriter.cs:在Logging命名空间中定义一个名为ILogWriter的接口。此接口规定了任何日志目标都必须遵循的契约,并且应包含一个Write(string message)方法,该方法返回一个字符串,用于说明消息写入了哪里。LogWriters.cs:在Logging命名空间中创建ILogWriter的两个具体实现:ConsoleLogWriter:它的Write方法返回Console: {message}FileLogWriter:通过构造函数接收文件名,它的Write方法返回File[{filename}]: {message}
Logger.cs:在Logging命名空间中创建一个Logger类,通过构造函数注入接收依赖。Logger应该:- 通过构造函数接收一个
ILogWriter,并将其存储在私有字段中 - 包含一个
Log(string message)方法,该方法调用注入的写入器,并返回结果
- 通过构造函数接收一个
Program.cs:通过创建两个不同的日志记录器来演示依赖注入,一个使用ConsoleLogWriter,另一个使用FileLogWriter。展示同一个Logger类如何根据注入的依赖不同而表现不同。
你将收到两个输入:
- 文件日志记录器的文件名
- 要记录的消息
创建一个使用 ConsoleLogWriter 的 Logger,并记录这条消息。然后创建另一个使用 FileLogWriter 的 Logger(使用输入中的文件名),并记录同一条消息。将每个结果分别打印在单独的一行上。
例如,如果输入是 app.log 和 System started,输出应为:
Console: System started
File[app.log]: System started注意,Logger 类从未改变,它可以与任何 ILogWriter 实现配合使用。这就是依赖注入的核心:Logger 依赖于一个抽象(接口),而具体实现则从外部“注入”。之后,你可以轻松添加 DatabaseLogWriter 或 CloudLogWriter,而完全无需修改 Logger 类!
亲手试试
using System;
using Logging;
class Program
{
public static void Main(string[] args)
{
// 读取输入
string filename = Console.ReadLine();
string message = Console.ReadLine();
// TODO:使用 ConsoleLogWriter 创建一个 Logger,并记录消息
// 打印结果
// TODO:使用 FileLogWriter(使用 filename)创建另一个 Logger,并记录相同的消息
// 打印结果
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C# 在线编译器