6

I like to invert dependencies whenever possible by depending mostly on abstraction and allowing the concrete implementations to be passed into the object by clients, or a factory. I've found this to be pretty conducive to testability and extensibility. Here's a simple example:

public class Feature {
    private final IStrategy strategy; // interface

    public Feature(IStrategy strategy) {
        this.strategy = strategy;
    }
}

However, I don't like to burden clients of this class with supplying the concrete implementation of IStrategy, because usually the client doesn't care. I also don't want to provide a factory every time I do something like this, because I would end up with a LOT of factories. Here's what I usually like to do instead:

public class Feature {
    private final IStrategy strategy; // interface

    public Feature(IStrategy strategy) {
        this.strategy = strategy;
    }

    public Feature() {
        this(new DefaultConcreteStrategy());
    }
}

Technically, Feature has a direct dependency on DefaultConcreteStrategy because it mentions it by name. The compile-time dependency will always be there. But the runtime dependency is effectively optional because motivated clients (unit tests, usually) can inject another concretion if desired.

Is this sound design? Is there a name for this pattern? Does anyone have a better or alternative approach?

2 Answers 2

6

Is there a name for this pattern?

Let's start with this question as the its name gives a succinct answer to your more general question. This is known as the Bastard Injection (Anti) Pattern. Unhelpfully, the term "poor man's DI" is also sometimes used to describe it, despite that term also commonly being used to describe pure/vanilla DI.

Unsurprisingly, the consensus is pretty much that the bastard injection pattern is not good design, save for one use case. If you have a legacy code base that doesn't employ the dependency inversion principle, then it makes sense to add new constructors that support DI whilst leaving the existing tightly coupled constructors in place as the code is gradually improved.

For a really good description of why this is bad design elsewhere, have a read of Injection anti-pattern: multiple constructors.

However, I don't like to burden clients of this class with supplying the concrete implementation of IStrategy, because usually the client doesn't care.

You shouldn't be burdening clients like this. Doing so still creates coupling and, whilst parts of the code still use dependency injection, you are still violating the dependency inversion principle. You should have just one point in the code (close to "main") that is responsible for resolving concrete classes, ie the composition root. Whether that's handled via pure DI or a container doesn't much matter. What's important is that dependency resolution is centralised. At that point, default constructors aren't then needed for injectable types.

9
  • 2
    I think "poor man's DI" is different from Bastard Injection. I've always understood "poor man's DI" as just doing Dependency Injection without using a fancy IOC Container.
    – Blake
    Commented Jan 30, 2020 at 12:43
  • 2
    Both your answer and the linked article talk a lot about 'violating the principle' but don't really explain what practical negative side effect this has.
    – Rotem
    Commented Jan 30, 2020 at 13:14
  • 1
    @Rotem, adding a default constructor to a type, which then handles its own dependencies, results in direct coupling within that type. This is a basic violation of the dependency inversion principle. I'd suggest that explaining that principle and how to violate it is beyond the scope of an answer to this question. But I'm happy to expand my answer if you think this would be useful.
    – David Arno
    Commented Jan 30, 2020 at 13:23
  • 1
    I think I see my answer in the article : "This approach makes the application inflexible since replacing, wrapping or intercepting any of the given dependencies can lead to sweeping changes throughout the application. "
    – Rotem
    Commented Jan 30, 2020 at 14:52
  • 1
    The additional default constructor is effectively a factory method and that is most certainly not an anti-pattern. And given that the OP already has a compile-time dependency on the concrete strategy, there is no practical benefit to pulling this factory method out into its own class (it is certainly possible and easy to pull it out if the need arises, and enabling change is the driving force behind design principles).
    – casablanca
    Commented Jan 31, 2020 at 6:58
0

Not sure if there is a name for this pattern, however we do this a lot on our project and there's nothing wrong with it as long as the rest of your class is designed well. If you're doing TDD, you will start with a constructor that takes in your dependencies and that should give you all the benefits of dependency injection. Adding a convenience constructor after you're done implementing the class doesn't invalidate those benefits.

Update: One of the comments brought up the issue of having a concrete dependency on a certain class. The assumption here (which is true for the OP) is that a compile-time dependency already exists on the concrete type and hence the default constructor is merely a convenience. Given that the class also supports injecting dependencies, it is possible to wire up a completely different dependency graph without changing any of the classes. And eliminating the concrete dependency is as simple as removing the default constructor. In short, this pattern introduces no maintenance overhead.

On the other hand, forcing every client to inject a dependency when most of them are fine with the default can end up violating DRY. The general solution to this is to introduce a factory, and a convenience constructor serves the same purpose without introducing an unnecessary factory class.

4
  • 1
    "Not sure if there is a name for this pattern" Convention over configuration? Commented Jan 30, 2020 at 8:48
  • 1
    The concrete type could be an issue, since that concrete type is a dependency of the class, and therefore a dependency of all clients that use this class. Commented Jan 30, 2020 at 16:30
  • @GregBurghardt: See my update to the answer.
    – casablanca
    Commented Jan 31, 2020 at 6:26
  • @DavideVisentin: Convention over configuration is probably a good way to phrase it. Many libraries often provide multiple constructors in this fashion: one that picks good defaults and another that allows customization.
    – casablanca
    Commented Jan 31, 2020 at 6:32

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.