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



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

让我说明一下我的问题:

套接字管理器类:

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 来"完成"套接字管理器的初始化之外,我不知道任何其他方法。然而,这是一个泄漏的抽象,如这篇文章所述:是否有一种模式来初始化通过 DI 容器创建的对象

最好从一开始就编写完整的对象图。应防止注入null值,因为它会使消费类复杂化。

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

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

博客文章中给出的解决方案是使用"上下文"抽象。例如,在您的例子中,一个SocketContext接口,它允许您在调用使用者的方法后,从而在构造使用者的对象图之后,由使用者获取Socket运行时值。例如:

public interface SocketContext
{
Socket get_CurrentSocket();
}

另一种选择是使用隐藏实际Socket或实际SocketManager的代理类(取决于可以放置代理的级别(。这允许使用者不知道某些运行时数据需要在后台初始化,并且在进行第一次调用后可能会延迟完成。例如:

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

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

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

相关内容

最新更新