依赖注入与对象创建:如何在不违反DI的前提下创建对象?
依赖注入中对象创建的常见问题与合规方案
工厂模式是否违反DI规范?
工厂模式本身不是反模式,也不违反DI的核心原则——只要你遵守以下两点:
- 工厂本身是通过DI注入的,而非在业务类中直接实例化;
- 工厂依赖的是抽象接口而非具体实现类。
DI的核心是控制反转(IoC)和依赖反转原则(DIP):将对象的创建权从业务代码中剥离,交给外部容器或注入的抽象组件。注入工厂本质上是把“对象创建逻辑”封装成一个可替换的抽象依赖,完全符合DI的设计思想。
不破坏DI的对象创建方法
1. 注入抽象工厂
将对象创建逻辑封装到实现了抽象接口的工厂类中,再把工厂注入到业务类里。这种方式既保留了DI的灵活性,又能延迟对象的创建时机(避免启动时初始化所有依赖)。
示例代码(C#):
// 定义抽象工厂接口 public interface IUserFactory { IUser CreateUser(); } // 具体工厂实现(可被DI容器管理) public class UserFactory : IUserFactory { private readonly ILogger _logger; // 工厂自身也可以注入依赖 public UserFactory(ILogger logger) { _logger = logger; } public IUser CreateUser() { // 这里创建具体实现,但依赖的是抽象ILogger return new User(_logger); } } // 业务类依赖抽象工厂 public class UserService { private readonly IUserFactory _userFactory; public UserService(IUserFactory userFactory) { _userFactory = userFactory; } public void ProcessUser() { // 按需创建对象,而非启动时初始化 var user = _userFactory.CreateUser(); user.DoBusinessLogic(); } }
2. 利用DI容器的延迟解析功能
大多数成熟DI容器(如Microsoft.Extensions.DependencyInjection、Autofac)都支持通过Lazy<T>或Func<T>实现延迟创建,避免启动时实例化所有对象:
- Lazy
:对象在第一次访问 Value属性时才被创建,适合单例或范围实例的延迟初始化:
public class OrderService { private readonly Lazy<IOrderRepository> _lazyOrderRepo; public OrderService(Lazy<IOrderRepository> lazyOrderRepo) { _lazyOrderRepo = lazyOrderRepo; } public void GetOrder(int id) { // 此时才触发对象创建 var repo = _lazyOrderRepo.Value; var order = repo.GetById(id); } }
- Func
:每次调用委托都会创建一个新实例(具体取决于容器的生命周期配置),适合需要多实例的场景:
public class OrderService { private readonly Func<IOrderRepository> _orderRepoFactory; public OrderService(Func<IOrderRepository> orderRepoFactory) { _orderRepoFactory = orderRepoFactory; } public void ProcessMultipleOrders() { // 每次调用都生成新的仓储实例 var repo1 = _orderRepoFactory(); var repo2 = _orderRepoFactory(); } }
3. 基于范围的对象创建
对于Web应用,可利用DI容器的范围生命周期(如ASP.NET Core中的AddScoped),让对象在请求到来时才实例化,请求结束后自动释放,避免启动时一次性创建所有对象:
// 在DI容器注册时配置为Scoped services.AddScoped<IOrderRepository, SqlOrderRepository>();
当控制器或业务类注入IOrderRepository时,容器会为每个HTTP请求创建一个新的实例,启动阶段不会初始化该对象。
4. 避免使用服务定位器(反模式)
服务定位器虽然能实现对象的动态创建,但它会隐藏业务类的依赖关系,破坏DI的透明性,导致代码难以测试和维护,因此不推荐使用。
核心原则总结
所有合规的DI对象创建方式,都必须遵守依赖抽象、控制反转的核心:
- 永远不要在业务类中硬编码实例化具体类;
- 将对象创建逻辑交给外部容器或注入的抽象组件;
- 确保依赖关系清晰可见,便于测试和替换。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

