Start The World's Best Introduction to TDD... free!

Dependency Inversion Principle (DIP) Comments

I recently received this comment at IBM DeveloperWorks about my now-very-old article, Use your singletons wisely. I really like knowing that people continue to read that article, since it was the first I published online somewhere other than my own web site.

Brent Arias, also known as mystagogue, wrote:

There is such a thing as singletons, in some languages, that can be “property” dependency injected, which essentially nullifies the suggested reason (viz. testability) for avoiding them (I do this with C#). Also, Liskov Substitution is not violated by singletons, because Liskov Substitution does not apply to constructors (which in part is why there is no such thing as “virtual constructors”). Finally, dependency injection is not without its own hazards. For example, many interim classes are forced now to take dependencies they don’t need, simply to pass them on to child classes which do need them. As such, dependency injection actually increases coupling between classes!!!

I’d like to thank Brent for his comment. Of course, I didn’t know then what I know now, so I didn’t mention the points that Brent raised in his recent comment. I’d like to address those points now.

The Testability Problem

First, Brent says that since he can inject a dependency through a property in C#, there is no testability problem. I disagreed with this in JUnit Recipes, recipes 14.3 and 14.4, which cover testing singletons and their clients. I agree that being able to inject the singleton instance makes run-time substitution in the tests possible, it defeats the purpose of designing the class as a singleton. Yes, you can resort to making the setInstance() method or Instance property writable only in the tests, but this serves only to complicate the design, rather than simplify it, and so I’d typically prefer to use this as an intermediate step towards a simpler design.

About LSP…

Next, Brian says that since the Liskov Substitution Principle does not apply to constructors, then a singleton-based design does not violate the principle. I had never thought about this before, so I researched the topic a little. The principle states that any property provable about a type must also be true of any subtype of that type. I suppose that, since constructors belong to the type class and not objects of the type, then Brent is correct: LSP does not apply to constructors. Even so, I had written this:

[…] any change in how the supplier class is instantiated ripples into the client class. This violates the Liskov Substitution Principle, which states that you should allow any application the freedom to tell the client class to collaborate with any subclass of the supplier.

I had written about the consequences of LSP, and not the principle itself, in rather a sloppy fashion. While one can subclass a singleton in a way that respects LSP, in order to use that subclass, one would have to make a choice between two poor alternatives: change clients to invoke Subclass.getInstance() instead of Superclass.getInstance() or change Superclass.getInstance() to become a proper Factory (in the Design Patterns sense) for the Superclass hierarchy. Choosing the first option violates the benefit of LSP, if not LSP itself: when you violate LSP you make changes that ripple out into your client, and so to avoid affecting your client, among other things, respect LSP. Choosing the second option leads you to a Singleton/Factory hybrid in which Superclass knows about Subclass, violating even more fundamental design principles, like Open/Closed. I don’t like either choice. I wish I had written this instead of what I wrote in the original article.

I Just Don’t Have This Problem

Finally, Brent says that, with dependency injection, “many interim classes are forced now to take dependencies they don’t need, simply to pass them on to child classes which do need them”. I agree with Brent, but strongly point out that dependency injection uncovers symptoms like these of deeper design problems, rather than creating these design problems in the first place. Unfortunately, I don’t have a concrete example to offer you, so I have to resort to one of those hopelessly abstract examples. I hope you’ll bear with me. If I find a better example, I’ll use it.

Consider three classes, unimaginatively named A, B and C. In our design, A uses B, and then B uses C. Since this design doesn’t yet inject dependencies, we have something like this:

class A {
  public A() {
    this.b = new B();
  }
}

class B {
  public B() {
    this.c = new C();
  }
}

So far, so good. (Well, not really, but good enough for now.) Now we want to switch to a dependency injection-based design to make it collaborators more pluggable. (I won’t bother with the reasons here; in a purely abstract example, there are no reasons.) This means that I first inject C into B through its constructor:

class B {
  public B(C c) {
    this.c = c;
  }
}

Now A needs to give B a C, but where does the C come from?

class A {
  public A() {
    this.b = new B(new C());
  }
}

The Problem, as Brent Describes It

This doesn’t look right, so we could decide to make the client give A a C, so that A can pass that on to B:

class A {
  public A(C c) {
    this.b = new B(c);
  }
}

class Client {
  someMethod() {
    A a = new A(new C());
  }
}

If Injecting Worked Once…

But now, of course, we have the case where A knows about C even though A doesn’t use it directly. If I understand Brent correctly, then this is his objection. One problem: why does A create the B directly? Why not inject that, too?

class A {
  public A(B b) {
    this.b = b;
  }
}

Now the client, which was already creating a C, simply creates a B, too:

class Client {
  someMethod() {
    A a = new A(new B(new C()));
  }
}

This way, A need not know anything about C, although if A eventually does need to use C directly, it can easily do so:

class Client {
  someMethod() {
    C c = new C();
    A a = new A(new B(c), c);
  }
}

This duplication, however, leads me to ask a few questions:

Do A and B use C differently? If they do, then maybe I should split C into two classes, D and E:

class Client {
  someMethod() {
    A a = new A(new B(new D()), new E());
  }
}

Do A and B use C for a similar reason? If they do, then maybe A and B duplicate some effort. Since A is B’s client, I would probably move that behavior from B to A, so that B no longer uses C at all:

class Client {
  someMethod() {
    A a = new A(new B(), new C());
  }
}

Is there some combination of both? If so, then likely I need to do both. Fortunately, that’s just another formulation of the first possibility.

There might be more questions, but that will do. While injecting the dependency appeared to make the classes more tightly coupled, it simply revealed the coupling that already existed. A was already tightly coupled to B and B to C. When we tried to inject the dependency, we made that coupling more explicit and easier to remove.

So thanks, Brent, for your comment, as it gave me an opportunity to follow up on my thinking about Singletons and design. I hope this response helps you understand why I don’t worry about the issues you brought up. Thanks, as well, for pointing out the error in my exposition about the Liskov Substitution Principle. I freely admit that, as a new face at the time, I mostly wanted to sound smart.

Comments