2

Based on this question, THE way to initialize new object with runtime parameters when using ioc-container is to create Abstract Factory.

In my example, I have this class:

internal sealed class AssetsDownloadingProcess
{
    private readonly IBackgroundWorker _backgroundWorker;
    private readonly IAssetsStorage _assetsStorage;
    private readonly Parameters _parameters;

    public AssetsDownloadingProcess(IBackgroundWorker backgroundWorker, 
        IAssetsStorage assetsStorage, Parameters parameters)
    {
        _parameters = parameters.Clone();

        _backgroundWorker = backgroundWorker;
        _assetsStorage = assetsStorage;
    }
}

And a factory to construct it:

internal sealed class AssetsDownloadingProcessFactory
{
    private readonly IBackgroundWorker _backgroundWorker;
    private readonly IAssetsStorage _assetsStorage;

    public AssetsDownloadingProcessFactory(IBackgroundWorker backgroundWorker,
        IAssetsStorage assetsStorage)
    {
        _backgroundWorker = backgroundWorker;
        _assetsStorage = assetsStorage;
    }

    public AssetsDownloadingProcess CreateProcess(
        AssetsDownloadingProcess.Parameters parameters)
    {
        return new AssetsDownloadingProcess(
            _backgroundWorker, _assetsStorage, parameters);
    }
}

as you can see, AssetsDownloadingProcess does not implement any interface and will never be replaced with another class. Therefore this factory is nothing more than useless piece of code. It could be completely omitted in favour of AssetsDownloadingProcessFactory constructor. However, then I can't use dependency injection to constructor.

I would like to use benefits of Injection from my IoC Container without the hassle of creating factory and creating useless code. What is the correct way to do this? Am I missing something or using DI wrong?

Steven
  • 166,672
  • 24
  • 332
  • 435
Mateusz Krzaczek
  • 614
  • 5
  • 17

1 Answers1

3

In general, you should prevent using runtime data to create and initialize your application components, as described here. The mere fact that runtime data is passed in through the constructor forces you to create a Factory.

The solution given by the article to the problem of injecting runtime data into components is to let runtime data flow through method calls on an initialized object graph by either:

  • pass runtime data through method calls of the API, or
  • retrieve runtime data from specific abstractions that allow resolving runtime data.

When runtime data is not used during object graph construction, the component can be created using DI inside the Composition Root and you problem therefore goes away.

Doing so however is not always feasible, and when it isn't, an Abstract Factory is the solution.

Since Object Composition should solely take place in the application's Composition Root however, this means that your Abstract Factory must be an Abstraction. Only this way can you prevent that the construction of your AssetsDownloadingProcess component takes place inside the Composition Root.

The way to do this is to:

  1. Define an abstraction, i.e. IAssetsDownloadingProcessFactory in the application layer where the consumers of this factory live.
  2. Create an implementation of this abstraction inside the Composition Root.

Not using an abstraction means that consumers will take a dependency on the concrete AssetsDownloadingProcessFactory class (which is a Dependency Inversion Principle violation) and it pulls the object composition out of the Composition Root. This will cause object composition to be scattered throughout the application, which hinders maintainability.

Steven
  • 166,672
  • 24
  • 332
  • 435