3

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;
  }
}
3
  • 4
    "It would be very expensive (let alone complicated) to recreate all the possible blocks of the workspace." This seems to be an assumption. What makes you so sure that modifying the parameters is faster than recreating them? In a garbage collected environment, keeping objects around for a long time can actually create more work for the garbage collector. What part of creating these do you think is expensive? Have you done any performance analysis on this?
    – JimmyJames
    Apr 10, 2018 at 20:30
  • 2
    "We can’t dispose a neuron and create another one with the desired internal strategy just to maintain constructor injection." We can't? Why not?
    – JimmyJames
    Apr 10, 2018 at 20:31
  • 1
    Constructor injection and Method injection are just forms of parameterization. Apr 10, 2018 at 22:23

4 Answers 4

2

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

There is a kind of rule in dependency injection literature, stating that we should declare all arguments in the constructor

Where? In which literature?

Given your examples, if _input is, in fact, run-time input, why don't you just write the class like the following?

public class Transformation
{
    private readonly IConditionStrategy condition;

    public Transformation(IConditionStrategy condition)
    {
        this.condition = condition;
    }

    public ISource Transform(ISource input)
    {
        ISource source = new Source();
        foreach(var i in input)
            if(input.Check(i))
                source.Add(i);

        return source;
    }
}
6
  • So, given the OP's requirements, you're saying that, instead of property-injecting a new strategy, you simply pass the strategy by reference, and then modify it in-place? Apr 10, 2018 at 19:57
  • @RobertHarvey No, where did I write that? To be honest, though, I don't really understand what that code is supposed to do, given that I don't know what ISource is... Apr 10, 2018 at 19:59
  • Presumably, the IConditionStrategy you injected would be used in some way to transform ISource (which you haven't illustrated in your code example). The OP states that the condition strategy might change during Transformation's lifetime. The only way that happens in your example is if you modify condition by reference. Apr 10, 2018 at 20:01
  • @RobertHarvey You may be right. I must admit that it's not really clear to me... (So, why did I answer the question, then? I was kindly requested to take a look at it, and I did my best.) Apr 10, 2018 at 20:03
  • Nice to see you here. Thanks for the contribution. Apr 10, 2018 at 20:04
2

I think your question and concerns are based on a confusion between dependency injection and immutability. These are two distinct concerns.

  • Dependency injection is the idea that instead of objects retrieving their dependencies, they are pushed to the object from the outside. This has benefits including ease of testing and improved reuse potential.

  • Immutability is the idea that objects, once created, are fixed. Their state cannot be modified.

When you put these together, it leads to constructor injection because, per definition, the object cannot be modified after construction. Immutability is preferred mainly due to correctness, especially when multi-threading which is increasingly more common due to hardware trends (i.e. the flattening of the Moore curve.) It can also have major performance benefits in a multi-threaded context. This is due to the fact that an immutable object can be kept in a thread's memory cache indefinitely without any need to synchronize its state across threads (because it never changes.) When the state of a local thread cache of an object is invalidated, the thread must wait while it is refreshed before it can access that objects state again. While this fast in human time scales it is fairly slow in computer time scales. In addition to this, some modern VMs can optimize code depending on immutable objects and eliminate the objects entirely by extracting values and placing them on the stack. I'm not sure these kinds of optimizations exist in the CLR today but I would expect that they would be added in the future.

So the concerns you have about performance are not completely unfounded but you might find that in practice the cost of immutability is less than you think. They might even be faster. For example, garbage collectors are typically generational. Without going into all the details: objects start life in an area where there is basically no cost to collecting them if they don't hang around very long.

As far as the specific example you give, if you were really concerned about performance, I would consider a Flyweight approach which means you create a permanent set of primitive Condition objects and then compose them as needed. The cost of creating a new object with references to these objects is unlikely to be a significant drag on performance but you should do that testing to really know.

26
  • This is wrong. if you inject references as with most constructor injection, you can alter the object you injected after injection. Hence the 'injectee' is not immutable simply because you used constructor over property injection
    – Ewan
    Apr 11, 2018 at 6:29
  • 1
    @Ewan Sorry, you are confused. I didn't make that claim.
    – JimmyJames
    Apr 11, 2018 at 13:34
  • 1
    @Ewan When you put immutability together with injection, yes. If you can modify an object, it's not immutable. Do you think otherwise?
    – JimmyJames
    Apr 11, 2018 at 13:41
  • 1
    @Ewan "My problem with the interpretation is that constructor injection DI doesn't give you immutable objects." You are correct that it doesn't. Immutability leads to constructor injection. There is nothing in my answer remotely like what you are claiming you see.
    – JimmyJames
    Apr 11, 2018 at 13:50
  • 1
    @Ewan So are you not understanding what it means to "put things together"? I said these are separate issues but when you "put them together" (i.e. combine them) you are forced to use constructor injection. I don't know how to make this more clear. A Venn diagram?
    – JimmyJames
    Apr 11, 2018 at 15:27
-1

Your example would be better served with no dependency at all. eg.

ISource Transform(ISource input)

However, yes you are right. sometimes you have a dependency, which is a real dependency of the class, which would normally be naturally constructor injected. But in a particular case you need to change it after the object has been instanciated.

If you had used Property Injection you would be unconstrained and could simply set it to another thing you say! But actually, no. Maybe you have to dispose of that things resources before you replace it, but maybe its used elsewhere, what about thread safety etc etc.

Constructor injection beats setter injection for only one reason

  1. You don't have to label up your objects telling things what should be injected

You can still pass nulls in as dependencies, you can still keep track of the objects you pass in and change them from outside the object

When you are faced with the problem of wanting to change a dependency, its not really constructor injection that prevents you its what you gave chosen to inject.

Say for example I have a database repository and I want to be able to switch it to another database during run time.

Normally I would dispose of one and fire up another, passing in the connection string as a dependency.

But theres nothing stopping me adding

ChangeDatabase(string connstr)

Methods. Its just that if I do, I need to add a whole set of checks for bad, closed or whatever connection and appropriate error handling on the other methods.

When I don't need that functionality dependency injection, and constructor injection in particular, allows me to skip those checks. If the object exists then it should have a connection string.

It makes the object simpler and causes it to fail faster if there is a problem.

10
  • 5
    Constructor injection beats setter injection for only one reason... -- Not quite. Constructor injection is associated with immutable objects. Apr 10, 2018 at 19:58
  • ahh but if you pass in a reference type and keep a reference to it, say a list, you can alter it from outside
    – Ewan
    Apr 10, 2018 at 20:01
  • Yes, I'm having that discussion below Mark Seeman's answer right now. (He's the author of "Dependency Injection in .NET," by the way. Good book.) Apr 10, 2018 at 20:04
  • hmm I think my point is different, or rather that immutable construction requires value types, which is not usual for DI
    – Ewan
    Apr 10, 2018 at 20:12
  • True immutability in an object-oriented language is harder than most people think it is. You don't need to pass value types, but you do need to pass objects that are themselves immutable. Apr 10, 2018 at 20:32

Your Answer

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

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