2

All guides and blog posts that I have read about dependency injection use a simple example where one object A depends on one object B. They apply DI by creating object B somewhere else and passing it to A via a setter/constructor, instead of having object A create and maintain object B directly.

Such examples, however, assume that:

  • A only ever needs a single instance of B
  • B can be created statically and ahead of time, without any information from A.

Now my questions are:

  • How is DI applied if A needs multiple instances of B? Do I simply pass a list of B objects to A's constructor or setter method?
  • How is DI applied if B cannot be created ahead of time, but only when 'A' actually has a need for it?
  • How is DI applied if B needs some information from A in order to be created?

In my specific example I have a component that, when requested to perform an authentication, generates an authentication packet and sends it to a remote server via a socket. To unit test this component without actually creating a socket and a remote server, I believe DI would make sense. That way I can inject a mocked socket for test cases and a real socket in real cases.

My situation diverges from the simple DI examples I've seen because:

  • The component needs multiple sockets, not one.
  • The sockets cannot be created ahead of time, because that wastes resources. They need to be created when an authentication request occurs.
  • Based on the specific authentication request, the component might decided to use a specific source address or port, which needs to be passed to the socket creation.
2
  • 2
    You don't inject sockets, right? You inject an authentication component, or a client component. This isn't a DI question, it's an OO question. Do objects intected into (or passed to) constructors have to be up and running? Of course not.
    – CodeCaster
    Commented Oct 27, 2022 at 12:57
  • 1
    My question is why do you think sockets must be injected by the container/application? Why cannot be just created on demand (say by factories)?
    – Laiv
    Commented Oct 27, 2022 at 15:59

4 Answers 4

4

This is a textbook use case for the abstract factory pattern. Let's look at your case (I'm going to write this as C# but it'll be the same principle most OO languages):

public class Component
{
    public Component(/*What do we pass here?*/)
    {
        // Initialization goes here
    }
}

The question "What do we pass here?" is saying "What does this class need, in order to be able to do its job?". Is the answer ISocket or List<ISocket>? Well, no, not according to the requirements you've written. Your class wants to be able to say:

  • Give me a socket on demand
  • Give me as many sockets as I ask for
  • Give me each socket configured in the way that I want

So take those as your requirements, what interface meets those requirements? It'd look something like:

public interface ISocketFactory
{
    public ISocket CreateSocket(string address, string port);
}

Now pass that into your class, and you can create your ports when you need them, and control their lifecycle within your component:

public class Component
{
    private readonly ISocketFactory _socketFactory;

    public Component(ISocketFactory socketFactory)
    {
        _socketFactory = socketFactory;
    }

    // An example of usage
    public void DoOperation(bool useSecondaryPort)
    {
        var port = useSecondaryPort ? 1000 : 1001;
        using(var socket = _socketFactory.CreateSocket("localhost", port))
        {
            // Use your port here
        }
    }
}

In your production code, you can define SocketFactory which passes Sockets, and in your test code, you can have a MockSocketFactory which produces MockSockets (or using a mocking library for this).


This pattern is called the abstract factory pattern. It's a "factory" pattern because you're using a factory object (an object whose job is to create other factories) and it's an "abstract factory" because you're passing an abstraction of a factory (in this case an interface) rather than a concrete factory class. And you're abstracting the implementation because you have multiple: one for real sockets and one for mocks.

It's good to know these common patterns, but note that we were also able to reason our way to the right answer just by asking the question "what does this class need?".

3

All guides and blog posts that I have read about dependency injection use a simple example where one object A depends on one object B. They apply DI by creating object B somewhere else and passing it to A via a setter/constructor, instead of having object A create and maintain object B directly.

If you'd like to know why study The law of Demeter (AKA The Principle of Least Knowledge).=

Such examples, however, assume that:

A only ever needs a single instance of B

Really?

A a = new A(new B("First of his name"), new B("The second"));

That code seems to work fine.

B can be created statically and ahead of time, without any information from A.

This is by design. Constructors intentionally encourage acyclic= designs. If you want a to know about b while b knows about a you're going to have to use setters. Be careful. Cyclic object graphs can be tricky to navigate and can cause hard to detect infinite loops.

How is DI applied if A needs multiple instances of B? Do I simply pass a list of B objects to A's constructor or setter method?

Don't see why not.

How is DI applied if B cannot be created ahead of time, but only when 'A' actually has a need for it?

See The Abstract Factory Pattern= and the like.== Also read up on what a composition root is.= DI doesn't just happen in main. Or just in the composition root. It happens as high up the call stack as you can get it. But no higher.

How is DI applied if B needs some information from A in order to be created?

You can usually work around cyclical dependencies between A and B with C. Sometimes called Context.=

In my specific example I have a component that, when requested to perform an authentication, generates an authentication packet and sends it to a remote server via a socket. To unit test this component without actually creating a socket and a remote server, I believe DI would make sense. That way I can inject a mocked socket for test cases and a real socket in real cases.

My situation diverges from the simple DI examples I've seen because:

The component needs multiple sockets, not one. The sockets cannot be created ahead of time, because that wastes resources.

What resources? These are mocks.

They need to be created when an authentication request occurs. Based on the specific authentication request, the component might decided to use a specific source address or port, which needs to be passed to the socket creation.

So? When you mock the sockets you create something that walks and talks like sockets but certainly doesn't do real socket work.

The trick here is to have two abstract factories. One for sockets. One for mock sockets. You can still create them whenever. But what is created depends entirely on which factory you injected.

11
  • I think PieterV is saying he wants to use dependency injection so that he can mock in the test case, but in the non-test case he can't create the sockets ahead of time because of wasting resources. I don't think he's concerned about the mocks wasting resources. Commented Oct 27, 2022 at 14:11
  • @BenAaronson if that's true then again, see the Abstract Factory Pattern. You can think of it like a button that constructs on demand whatever thingy you need without having to know what exactly the thingy is. All you need to know is when you need it. DI doesn't have to all happen in main. If it did you'd have no timestamps. Just get as high up the call stack as you can get. Commented Oct 27, 2022 at 14:18
  • Also exist the concept Provider or Supplier . Instead of injecting the dependency, we inject a component that will sort which instance must be used when provider.get() . But if you can't create the instances ahead of time, then the factory seems better solution.
    – Laiv
    Commented Oct 27, 2022 at 15:01
  • @Laiv I think I know what you're talking about but Google isn't cooperating. Know a good link to cite? Commented Oct 27, 2022 at 15:08
  • My idea of Supplier comes from Java Stream supplier functions. The Provider comes from an approach to Delta Spike's BeanProvider, Both have in common that the "strategy" to choose which bean/data to return is encoded in one of these components. It's solved in runtime and not necessarily during bootstrapping.
    – Laiv
    Commented Oct 27, 2022 at 15:19
0

How is DI applied if A needs multiple instances of B? Do I simply pass a list of B objects to A's constructor or setter method?

A list of B objects might work, but beware that the instances might be configured differently. This might justify two or more separate constructor parameters. There is no general rule here. This is highly dependent on the use case.

How is DI applied if B cannot be created ahead of time, but only when 'A' actually has a need for it?

How is DI applied if B needs some information from A in order to be created?

The last two questions can be addressed with a factory object, or builder (the Joshua Bloch variation). In the case that B cannot be initialized ahead of time, A needs a factory object that creates instances of B on demand. This factory object should have a method that accepts any runtime data required to initialize B. This factory method can be invoked whenever A has a need to, thus satisfying issues brought up in your last two questions. Dependency Injection comes in to play with the factory object — the factory object must be injected.

Things get a little fuzzy when you need to interact with some outside resource, like authentication or sockets. Sockets represent a concrete operation, rather than an abstraction. Socket connections would be an implementation details of whichever class that needs them. It is perfectly acceptable for a class to initialize however many sockets it needs at the time it needs them. The "injectable" stuff would be the configuration data necessary to establish those socket connections, for example the host name and port number.

The class that uses the socket connections is the abstraction that other classes need passed into them. DI comes into play at the level of an abstract operation, rather than something concrete. Typically an abstract class or interface is used to facilitate unit testing, so things that depend on the object that uses sockets do not need to create actual socket connections during unit tests.

Authentication is similar. It is a concrete operation that should be represented by an interface or abstract class to facilitate unit testing. Objects that need to send authentication requests should receive this "authenticator" as a constructor parameter, which ties into dependency injection and inversion of control.

The main thing to learn is the distinction between a dependency that needs to be passed in versus a dependency that can be initialized inside the class that needs it. Not every dependency needs to be created and passed in. Some dependencies, like sockets, are an implementation detail so closely tied to the basic reason that class exists that it becomes inconvenient or impossible to separate the two objects.

0

tldr;

How is DI applied if A needs multiple instances of B? Do I simply pass a list of B objects to A's constructor or setter method?

If you need multiple instances of the same object, then a common solution is to inject a provider or factory object that can later be used to access the specific instances that you need on demand. If you are manually injecting then you could use Named instances resolved from the names of the arguments or properties, but in most cases this will tightly couple the concepts, you're now closer to a Provider implementation and not really DI at all.

How is DI applied if B cannot be created ahead of time, but only when A actually has a need for it?

DI is a factory implementation that is invoked dynamically when object A is instantiated to resolve the instances of any dependent objects required either for the constructor or for the properties on the instance of object A that is being created.

At the time when A is created, DI looks for an appropriate instance of B, if no instance is found, it is created. It is not necessary and in many cases not very efficient to create all objects ahead of time and you should only do this if B is a singleton and your application absolutely needs to have the instance of B available from the start

How is DI applied if B needs some information from A in order to be created?

That is a key tenet of the DI pattern, to decouple the creation of B from A. If A is dependent on B, then B really cannot be dependent on A. Especially in DI Containers, dependency resolution occurs at the initialization stage, B needs to be instantiated first, to allow A to be created, at the time that B is created, A does not yet exist.

You cannot use DI to inject B into A in this scenario, you will need to go back to manual injection methods or redesign your classes to decouple them. The Dependency Injection Pattern helps you to identify and decouple these dependencies.

  • You can still use your DI frameworks to manually implement a co-dependency scenario but doing so is not really DI anymore.

Lets Explore DI:

DI is not specifically about creating object instances ahead of time, DI is an Inversion of Control (IoC) pattern built upon the Factory pattern that abstracts the creation of dependencies away from the implementation. A common implementation of DI is to use Dependency Containers to create object instances and automatically resolve their dependent arguments of properties through type registrations/configurations and conventions.

  • It is in this registration that the arguments for the creation of instances are resolved.
  • In many frameworks the configuration can be via XML, or other settings providers, it doesn't necessarily have to be code or at least your code that performs this registration.

So DI is about decoupling the logic that creates the dependent objects, not specifically ahead of time, it just manages the creation of and access to objects for the duration of specific scopes.

  • In Factory pattern you would explicitly call methods on the factory to provide or prepare object instances in a certain way, in DI we do not constrain or try to influence how the object was created, just the type and if needed, the name of the expected instance.

While many simple examples do cover Singleton implementations, most DI frameworks will allow you define a scope that will constrain the lifetime of the objects that are created.

Spring: When the Spring Framework creates a bean, it associates a scope with the bean. A scope defines the runtime context within which the bean instance is available. In Spring, a bean can be associated with the following scopes:

  • Singleton
  • Prototype
  • Request
  • Session
  • Global session
  • Application

Note: Out of the preceding scopes, Request, Session, and Application are for beans in Web-aware applications. Global session beans are for portlets.

You can configure your type to be instantiated for every new class instance, so not retain a common instance at all (prototype scope in Spring), it is also common in state-less web application design to use Request based scopes, where all objects created to service the same HTTP Request will share the same objects.

You can also manually request instances from the DI provider, this allows you to obtain instances on demand for custom or complex scenarios or to request specific named instance implementations if your framework supports them.

  • Using named instances allows multiple instances of the same type to be resolved from the DI provider, but due to the name variable it can't be used for default instance injection via Dependency Containers, you must manually request the instances.
2
  • "In DI you shouldn't be passing any objects at all, that is the point, DI Injects the object instances that are required" - nope, this is a common misunderstanding. It's DI Containers that automatically inject instances, but DI is a concept independent from containers, and you absolutely can apply DI without using a container, where you'd inject the dependencies manually by passing them. The power/purpose of DI is not in the container, it's in the design principle of being explicit about the dependency abstractions, and letting some code outside of the class handle their creation. Commented Oct 27, 2022 at 16:56
  • 1
    Thanks @FilipMilovanović thats pretty much what I was trying to say, but you're right in that passage I had assumed OP was using containers, mainly from the use of the keyword 'dynamically' in the title. Commented Oct 27, 2022 at 17:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.