2

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

Steven
  • 166,672
  • 24
  • 332
  • 435
muley
  • 41
  • 5
  • Could you explain a little bit more? Maybe add some code... What DI framework are you intending to use? – fabiojb Sep 26 '18 at 00:07
  • @FabioBohnenberger I added an example to illustrate my point ;-) – muley Sep 26 '18 at 11:53
  • What should happen if client code interacts with `SocketManager` *before* the socket is available? Could there be multiple concurrent sockets? Can sockets go away again? – Mark Seemann Sep 26 '18 at 17:40
  • No, there is just one SocketManager with one Socket and sockets can't go away. I started the project without any DI framework, so `Socket` was always created relatively early in the program. I then passed the`SocketManager` down the entire stack through constructors wherever it was needed, obviously this was bad. So in practice at the time `RandomClass` is using the SocketManager it will be fully initialized. How do your questions relate to my illustrated problem, if I may ask? – muley Sep 26 '18 at 19:05
  • I came to the conclusion, that a variable that is set at runtime will certainly at some point be NULL (no matter if you wrap it into a container or not). There's no point around. So, it's the programer's responsbility to make sure that this variable is first set, before it's called by a consumer. It's the tradeoff you have to accept, when going with DI frameworks, since a normal constructor chain will force you to specify a temporal chain, which guarantees a "happens-before" relationship. – muley Oct 01 '18 at 07:31

1 Answers1

0

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. Paragraph 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);
}
Steven
  • 166,672
  • 24
  • 332
  • 435
  • Thanks Steven, I added an example to illustrate my point. Let's take you ´SocketContext` solution. As I understand the SocketContext is similar to the SocketManager, so the same problem remains: How do you set-up the `SocketContext` at runtime? You would need to leave `Socket` at NULL and complete the initliazation of SocketContext at runtime. Only then you could use `get_CurrentSocket()`. How do I solve this problem without an init() or setter, like in the example? – muley Sep 26 '18 at 11:52
  • Seems to me you have the dependencies in the wrong direction. If you let `SocketManager` depend on `SocketCreator`, it can call the creator at the point that the `Socket` is first requested. – Steven Sep 26 '18 at 13:28
  • If `SocketManager` has a `SocketCreator` depenendency, then I could use the socket via the `SocketCreator`, is that what you mean? However, how do I assure that the SocketCreator is actually set, when I call it via SocketManager? Isn't that some sort of temporal coupling problem? Further, `Socket`inside `SocketCreator`would need to become a class member and be NULL at compile time during object graph initialization. – muley Sep 27 '18 at 02:32
  • Why must the creator be set? Isn't it the creators job to create? – Steven Sep 27 '18 at 05:42
  • Well, I guess we should **assure** that it is set before we access the socket, otherwise `SocketManager` would be in an inconsisten state. No, I should not have called it SocketCreator, but rather `SocketHolder` to make it clearer. It's basically only the class that has/gets the `Socket` information at runtime. Now I want to make this received socket available at various places in my code. I think there's no way around than storing it somewhere in a context object, which I can inject at compile time. However, this leads to the problem that `Socket` is NULL at compile time. – muley Sep 27 '18 at 07:23