在IoC容器中将依赖项设置为NULL并在运行时提供依赖项是不好的做法?

我有一个包含Socket和其他字段的SocketManager类。 除了Socket之外的所有字段都可以在对象图的组合期间使用DI框架注入。 我的想法是通过将Socket留空并在运行时设置它来简单地构建整个对象图。 这将允许我在代码中的某一点完成SocketManager实例化,并在整个程序中使用该实例(因为它已经通过DI框架设置为依赖)? 这是“注入”运行时依赖项的标准方法还是不好的做法?
抽象工厂似乎是一个坏主意,原因有两个:a)每次创建一个不同的对象b)它需要在我想要创建对象的每个地方运行时参数

让我来说明一下我的问题:

SocketManager类:

 public class SocketManager { //i'll only receive the socket at runtime Socket socket; //this object is available at compile-time and can be injected through the DI container InjectableObject obj; } 

在我的代码[CodePosition1]的某处我会收到这样的套接字:

 public class SocketCreator{ SocketManager socketManager; //will be injected through DI container at startup Socket socket = this.serverSocket.accept(); // at this point the socket manager is fully initialized socketManager.setSocket(socket); } 

在许多其他地方[CodePosition2]我现在可以使用SocketManager依赖项

 public class RandomClass { //injected at compile-time through DI container, but only usable after [CodePosition1] // was executed SocketManager socketManager; ... socketManager.getSocket().doSth() ... } 

问题是SocketManager没有完全初始化,直到[CodePosition1]在运行时,所以我不知道除了在SocketManager上使用init()或setter来“完成”SocketManager的初始化之外的任何其他方式。 然而,这是一个漏洞抽象,如本文所述: 是否存在初始化通过DI容器创建的对象的模式

最好从一开始就构建完整的对象图。 应该防止注入null值,因为它使消耗类复杂化。

但是,在您的情况下,似乎Socket不是“真正的”组件,而是运行时数据 。 如此处所述,您应该防止在构造期间将运行时数据注入对象图。

该文提供了两种解决该问题的解决方案,但还有更多解决方案。 然而,正如您已经提到的那样,抽象工厂通常不是一个好的解决方案,而且这篇博文更一般地描述了抽象工厂的问题。 本书第6.2章甚至从DI的角度详细介绍了抽象工厂的问题。

博客文章中给出的解决方案是使用“上下文”抽象。 例如,在您的情况下,一个SocketContext接口允许您在消费者的方法被调用之后,并且因此构建消费者的对象图之后 ,由消费者获取Socket运行时值。 例如:

 public interface SocketContext { Socket get_CurrentSocket(); } 

另一种选择是使用隐藏真实Socket或真实SocketManager的Proxy类(取决于您可以放置​​代理的级别)。 这允许消费者不知道某些运行时数据需要在封面下初始化,并且一旦第一次调用就可能懒得完成。 例如:

 public class SocketManagerLazyProxy : SocketManager { private SocketManager mananger; public void DoSomething() { if (manager == null) manager = new RealSocketManager(new Socket()); manager.DoSomething(); } } 

另一个选择是在构建对象图之后使用Property Injection设置Socket值。 这允许您更早地构建对象图,并在请求进入时设置运行时值,方法是在请求进入时设置它:

 void HandleRequest(RequestData data) { SocketManager manager = GetSocketManagerForThisRequest(); manager.Socket = new Socket(); Handler handler = GetHandler(data.Name); handler.Handle(data); }