.net 的依赖注入(Dependency Injection,简称 DI)是一种设计模式,其目的是将对象的依赖关系从对象本身解耦。
文章目录
.NET 中的依赖注入(Dependency Injection,DI)通过以下几种关键机制来实现对象解耦:
1、 传统方式:紧耦合
这种写法问题
1、代码高度依赖具体实现
2、难以更换数据库类型
3、不易进行单元测试
4、模块间强耦合
public class UserService {
// 直接依赖具体实现,导致高度耦合
// 直接依赖具体实现,导致高度耦合
private SqlServerDatabase _database = new SqlServerDatabase();
public void SaveUser(User user) {
_database.Save(user);
}
}
2、依赖注入的解耦方案
// 定义抽象接口
public interface IDatabase {
void Save(User user);
}
// 具体实现解耦
public class SqlServerDatabase : IDatabase {
public void Save(User user) {
// SQL Server 存储逻辑
}
}
public class MySqlDatabase : IDatabase {
public void Save(User user) {
// MySQL 存储逻辑
}
}
// 依赖注入的服务
public class UserService {
// 依赖抽象,而非具体实现
private readonly IDatabase _database;
// 通过构造函数注入依赖
public UserService(IDatabase database) {
_database = database;
}
public void SaveUser(User user) {
// 可以使用任何实现了IDatabase的具体类
_database.Save(user);
}
}
3、依赖注入的核心解耦机制
解耦机制包括:
a. 面向接口编程
b. 依赖倒置原则
c. 控制反转
// 依赖倒置原则示例
public interface ILogger {
void Log(string message);
}
public class UserService {
private readonly ILogger _logger;
private readonly IDatabase _database;
// 通过构造函数解耦
public UserService(
ILogger logger,
IDatabase database
) {
_logger = logger;
_database = database;
}
public void Process(User user) {
// 解耦:可以使用任何日志和数据库实现
_logger.Log("Processing user");
_database.Save(user);
}
}
切换 例如通过配置方式实现
// 接口定义
public interface IDatabase {
void Save(User user);
}
// MySQL 实现
public class MySqlDatabase : IDatabase {
public void Save(User user) {
// MySQL 存储逻辑
}
}
// SQLServer 实现
public class SqlServerDatabase : IDatabase {
public void Save(User user) {
// SQLServer 存储逻辑
}
}
// 启动配置
public void ConfigureServices(IServiceCollection services) {
// 方式1:通过配置文件动态选择
var databaseType = Configuration["DatabaseType"];
switch (databaseType) {
case "MySQL":
services.AddScoped<IDatabase, MySqlDatabase>();
break;
case "SQLServer":
services.AddScoped<IDatabase, SqlServerDatabase>();
break;
default:
throw new Exception("未知数据库类型");
}
// 方式2:hardcode方式
// services.AddScoped<IDatabase, MySqlDatabase>();
}
4、依赖注入容器的工作原理
// 依赖注入容器配置
public void ConfigureServices(IServiceCollection services) {
// 注册服务的不同生命周期
// 瞬时:每次请求都创建新实例
services.AddTransient<IDatabase, SqlServerDatabase>();
// 作用域:每个请求周期一个实例
services.AddScoped<ILogger, FileLogger>();
// 单例:全局唯一实例
services.AddSingleton<IConfigService, AppConfigService>();
// 注册服务
services.AddScoped<UserService>();
}
5、依赖注入的优势
// 优势体现
public class TestUserService {
// 可以轻松进行单元测试
public void TestSaveUser() {
// 使用模拟对象
var mockDatabase = new Mock<IDatabase>();
var mockLogger = new Mock<ILogger>();
var userService = new UserService(
mockLogger.Object,
mockDatabase.Object
);
// 验证逻辑
userService.Process(new User());
// 校验交互
mockDatabase.Verify(db => db.Save(It.IsAny<User>()), Times.Once);
}
}
6、完整解耦示例
// 抽象定义
public interface IPaymentGateway {
bool ProcessPayment(decimal amount);
}
public interface INotificationService {
void SendConfirmation(string message);
}
// 具体实现
public class StripePaymentGateway : IPaymentGateway {
public bool ProcessPayment(decimal amount) {
// Stripe支付逻辑
return true;
}
}
public class EmailNotificationService : INotificationService {
public void SendConfirmation(string message) {
// 发送邮件通知
}
}
// 订单服务
public class OrderService {
private readonly IPaymentGateway _paymentGateway;
private readonly INotificationService _notificationService;
// 解耦:通过构造函数注入依赖
public OrderService(
IPaymentGateway paymentGateway,
INotificationService notificationService
) {
_paymentGateway = paymentGateway;
_notificationService = notificationService;
}
public void PlaceOrder(Order order) {
// 解耦的业务逻辑
if (_paymentGateway.ProcessPayment(order.Total)) {
_notificationService.SendConfirmation("订单成功");
}
}
}
总结
关键解耦原理总结:
依赖抽象接口,而非具体实现
通过外部注入依赖
使用接口定义契约
利用依赖注入容器管理对象生命周期
降低模块间直接耦合
这种设计使代码:
更加灵活
易于测试
方便扩展
解耦各个组件