You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

依赖注入与对象创建:如何在不违反DI的前提下创建对象?

依赖注入中对象创建的常见问题与合规方案

工厂模式是否违反DI规范?

工厂模式本身不是反模式,也不违反DI的核心原则——只要你遵守以下两点:

  1. 工厂本身是通过DI注入的,而非在业务类中直接实例化;
  2. 工厂依赖的是抽象接口而非具体实现类。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 11:10:40