Design – Concerns About Constructor Dependency Injection Logic

dependency-injectiondesignobject-oriented-design

There is a kind of rule in dependency injection literature, stating that we should declare all arguments in the constructor, in order to have a constructor injection, which is better than other approaches.

At first, there is nothing wrong with that.

But I’ve come to realize that this “rule” applies only to the realm of – let’s say – MVC controllers. Because a controller is spawned to do a certain job, doesn’t diverge from its purpose, and then disposes. In general, it applies to a 'request based system'.
Now, let’s say that we have a block that gets data, transforms them, and outputs the transformed data. This block has various other parameters that must be configured, such as an IConditionStrategy (Equals, GreaterThan etc..).

We configure the block and run the application. Then, we want to get some other results and so we change the parameters of that block (a good example of this is matlab’s simulink).
The block doesn’t dispose, it exists in a workspace. It has done its job (transforming the data) and waits in the workspace to rerun another job.
It would be very expensive (let alone complicated) to recreate all the possible blocks of the workspace.
But when we start to change its parameters, which can vary from numbers to various strategies, we must provide in code behind certain concrete implementations to this block that its interface needs.
In this case, how would the 'standard' constructor dependency injection fit?

Unfortunately, we have to get rid of the constructor, because we have to change the parameters at runtime. But then again, if we get rid of the constructor, we won’t be able to ensure that a parameter is always valid.
Should we provide a kind of default implementation? (We’ll fall in the service locator trap!)

And another thing that’s always bothered me. I understand the danger of a null appearing at runtime (so we should have constructor injection), but what do we actually gain when we create an object and then constrain it from changing its internal strategies, parameters etc. after its creation.
An answer to this could that we don’t have to change it at all, just dispose it and create another one. Sure, but I can’t see how this technique fits in domains other than 'request based systems', such as web apps.

Imagine that we have a neural network with neurons that have an internal strategy, for example change their transfer functions at runtime even while an identification or learning is in process. We can’t dispose a neuron and create another one with the desired internal strategy just to maintain constructor injection.
Of course, in various dependency injection books, other types of injection are mentioned, such as method injection, but I got the feeling that constructor injection should be used on 80% of the occasions according to literature. Given various applications, my estimate is that it has to be way lower than 80%.
I am misunderstanding something?
I would very much appreciate your opinions on the matter.

As example I give the following snippet.
The object needs to change at runtime, but the constructor injection constrains it.

public class Transformation {
  private readonly IConditionStrategy _condition;
  private readonly ISource _input;

  public Transformation(IConditionStrategy condition, ISource input) {
    this._condition = condition;
    this._input = input;
  }

  public ISource Transform() {
    ISource source = new Source();

    foreach(var i in this._input) {
      if(this._condition.Check(i)){
        source.Add(i);
      }
    }

    return source;
  }
}

In the following example we change the parameters at runtime. Because we can't just dispose and recreate another object with the desired parameters, we don't use the classical dependency injection practice.

public class Transformation{
  public IConditionStrategy Condition{get;set;}
  public ISource Input{get;set;}

  public Transformation() {
    // we could provide some default 
    // implementations just to avoid null
  }

  public ISource Transform(){
    ISource source = new Source();

    foreach(var i in this._input){
      if(this._condition.Check(i)){
        source.Add(i);
      }
    }

    return source;
  }
}

Best Answer

As @Mark noticed there no such rule that all arguments should be instantiated in the constructor.

Given various applications, my estimate is that it has to be way lower than 80%

I think it is not right to make design decision based on the percentage values. You can use the way to inject dependencies which fits requirements of current context.

In your particular example IConditionStrategy is dependency which could change during runtime - so why you not introduce it as "external" dependency and pass it to the method as argument?

public class Transformation
{
    public Transformation() { }

    public ISource Transform(IConditionStrategy condition, ISource input)
    {
        ISource source = new Source();

        foreach(var i in input){
             if (condition.Check(i))
             {
                 source.Add(i);
             }
        }

        return source;
    }
}

In constructor you can inject default strategy conditions which remain unchanged during transformation lifetime

public class Transformation
{
    private readonly IConditionStrategy _defaultCondition;

    public Transformation(IConditionStrategy defaultCondition) 
    { 
        _defaultCondition = defaultCondition;
    }

    public ISource Transform(ISource input)
    {
        return Transform(_defaultCondition, input);
    }

    public ISource Transform(IConditionStrategy condition, ISource input)
    {
        ISource source = new Source();

        foreach(var i in input){
             if (condition.Check(i))
             {
                 source.Add(i);
             }
        }

        return source;
    }
}
Related Topic