在IoC容器中将依赖项设置为NULL并在运行时提供依赖项是不好的做法? [英] Is it a bad practice to set dependencies to NULL in a IoC container and supply the dependencies at runtime?

查看:202
本文介绍了在IoC容器中将依赖项设置为NULL并在运行时提供依赖项是不好的做法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 SocketManager 类,其中包含 Socket 和其他字段。除了 Socket 之外的所有字段都可以在对象图的组合期间用DI框架注入。我的想法是通过将 Socket 清空并在运行时设置它来简单地构建整个对象图。这将允许我在代码中的某一点完成 SocketManager 实例化,并在整个程序中使用该实例(因为它已经通过DI框架设置为依赖项) ?这是注入运行时依赖性的标准方法还是不好的做法?

抽象工厂似乎是一个坏主意,原因有两个:a)它每次创建一个不同的对象b)它需要我想要创建对象的每个地方的运行时参数



让我说明一下我的问题:



SocketManager类:

  public class SocketManager {
//我只会在运行时收到套接字
套接字套接字;
//这个对象在编译时可用,可以通过DI容器注入
InjectableObject obj;
}

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

  public class SocketCreator {
SocketManager socketManager; //将在启动时通过DI容器注入
Socket socket = this.serverSocket.accept();
//此时套接字管理器已完全初始化
socketManager.setSocket(socket);
}

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

 公共类RandomClass {
//在编译时通过DI容器注入,但仅在[CodePosition1] $ b之后可用$ b //执行
SocketManager socketManager;
...
socketManager.getSocket()。doSth()
...
}

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

解决方案

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



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



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

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

  public interface SocketContext 
{
Socket get_CurrentSocket();
}

另一种选择是使用隐藏真实<$ c的Proxy类$ c> 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数据)
{
SocketManager manager = GetSocketManagerForThisRequest();
manager.Socket = new Socket();
Handler handler = GetHandler(data.Name);
handler.Handle(data);
}


I have a SocketManagerclass that contains a Socket and other fields. All fields except the Socketcan be injected during the composition of the object graph with a DI framework. My idea was to simply build the entire object graph upfront by leaving Socket empty and set it during runtime. This would allow me to complete the SocketManager instantiation at one point in the code and use that instance throughout my entire program (as it was already set as an dependency through the DI framework)? Is that the standard way to "inject" runtime dependencies or is it bad practice?
A abstract factory seems to be a bad idea for two reasons: a) it creates a different object everytime b) It requires the runtime parameters at every place where I want to create the object

Let me illustrate my problem:

SocketManager class:

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;
}

Somewhere in my code [CodePosition1] I will receive the socket like this :

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); 
}

At numerous other places [CodePosition2] I can now use the SocketManager dependency

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

The problem is that SocketManageris not fully initialized, until [CodePosition1] at runtime, so I don't know any other way than using a init() or setter on SocketManager to "complete" the initialization of the SocketManager. This is however a leaky abstraction, as explained in this post: Is there a pattern for initializing objects created via a DI container

解决方案

It's best to compose complete object graphs right from the start. Injection of null values should be prevented, because it complicates the consuming class.

In your case, however, it seems like the Socket is not a 'real' component, but rather runtime data. As described here, you should prevent injecting runtime data into the object graph during construction.

That article gives two solutions to work around that problem, but there are more solutions. Abstract factories are, however, typically not a good solution, as you already alluded, and this blog post describes in more general sense what the problem is with Abstract Factories. Chapter 6.2 of this book even goes into more details about the problem with Abstract Factories from a DI point of view.

A solution given in the blog post is the use of a 'context' abstraction. In your case, for instance, a SocketContext interface that allows you to get the Socket runtime value by a consumer, once that consumer's methods are called, and thus after the consumer's object graph is constructed. For instance:

public interface SocketContext
{
    Socket get_CurrentSocket();
}

Another option is to use a Proxy class that either hides the real Socket or the real SocketManager (depending on which level you can place the Proxy). This allows a consumer to be unaware that some piece of runtime data needs to be initialized under the covers and that it might be done lazily once the first call is made. For instance:

public class SocketManagerLazyProxy : SocketManager
{
    private SocketManager mananger;

    public void DoSomething()
    {
        if (manager == null) manager = new RealSocketManager(new Socket());
        manager.DoSomething();
    }   
}

Another option is to set the Socket value using Property Injection after the object graph is built. This allows you to construct the object graph much earlier in time, and set the runtime value once a request comes in, by setting it when a request comes in:

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

这篇关于在IoC容器中将依赖项设置为NULL并在运行时提供依赖项是不好的做法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆