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

依赖注入:复杂单例实例化是否需封装为工厂类?

回答你的两个问题

1. 即便只实例化一次,仍然建议封装到Factory类里

你的理解里有个小误区:Factory模式的核心价值不止是多次创建实例,更重要的是封装复杂的对象初始化逻辑,让代码更清晰、可维护,同时提升可测试性。

看你现在的CreateMyService方法,已经包含了不少细节:HttpClient的多层装饰、子服务的依赖组装。如果把这些逻辑放在一个专门的MyServiceFactory类里,好处很明显:

  • 职责单一:Factory类只负责创建IMyService及其依赖,和其他业务逻辑解耦;
  • 可读性提升:其他地方调用时,只需要知道调用factory.Create(...),不用关心内部怎么组装装饰器、怎么创建子服务;
  • 可维护性更好:未来如果要修改装饰器顺序、替换子服务的依赖,只需要修改Factory类,不用在原来的宿主类里找代码;
  • 便于测试:测试时你可以很容易地替换Factory(比如返回Mock的IMyService),或者在Factory里注入测试用的依赖,不用改动业务代码。

举个重构后的示例:

public class MyServiceFactory
{
    public IMyService Create(IService service, ILoggerFactory logFactory, IHttpClient client, IShareRessource sharedRessource)
    {
        var decoratedClient = new Decorator3(new Decorator2(new Decorator1(client))); 
        var subService1 = new SubService1(sharedRessource, logFactory.CreateLogger<SubService1>()); 
        var subService2 = new SubService2(sharedRessource, logFactory.CreateLogger<SubService2>(), service); 
        return new MyService(decoratedClient, subService1, subService2); 
    }
}

哪怕现在只创建一次,这种封装也能让代码更干净,而且如果未来需求变化需要多次创建(比如多租户场景),你不用大幅重构代码。

2. 不使用DI框架时,实例化这类复杂服务的常规位置

通常会把这类复杂依赖的创建放在**应用的组合根(Composition Root)**里——这是一个专门负责组装所有依赖的集中位置,一般是应用的启动入口附近:

  • 在C#里,可能是Program.cs的Main方法、或者Startup.cs(.NET Framework)/Program.cs的服务配置部分(.NET Core+);
  • 在Java里,可能是main方法、或者专门的AppConfig类。

组合根的核心思想是把所有依赖创建逻辑集中到一个地方,避免依赖的初始化代码散落在业务类里,导致维护困难。你可以在这里调用Factory创建IMyService实例,然后通过构造函数注入的方式把它传递给需要的地方,或者放到一个简单的容器(比如自己实现的轻量级容器,注意不要滥用服务定位器反模式)里供其他组件获取。

举个C#的示例(.NET 6+):

var builder = WebApplication.CreateBuilder(args);

// 注册基础依赖
builder.Services.AddSingleton<IService, ConcreteService>();
builder.Services.AddSingleton<IHttpClient, ConcreteHttpClient>();
builder.Services.AddSingleton<IShareRessource, ConcreteShareRessource>();

// 创建MyService实例
var factory = new MyServiceFactory();
var myService = factory.Create(
    builder.Services.BuildServiceProvider().GetRequiredService<IService>(),
    builder.Services.BuildServiceProvider().GetRequiredService<ILoggerFactory>(),
    builder.Services.BuildServiceProvider().GetRequiredService<IHttpClient>(),
    builder.Services.BuildServiceProvider().GetRequiredService<IShareRessource>()
);

// 把单例的MyService注册到容器,供其他组件使用
builder.Services.AddSingleton<IMyService>(myService);

var app = builder.Build();
// ... 后续配置

这样所有依赖的创建逻辑都集中在启动阶段,业务代码只需要通过构造函数接收IMyService,不用关心它是怎么来的。

内容的提问来源于stack exchange,提问作者corentinaltepe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:17:42