Dependency Injection gone too far?

Published 27 Feb 2012

I am currently working with a new version of a hobby console application project of mine. The app will execute certain actions depending on the input arguments.

I’m now wondering if I am taking dependency injection too far in this project. I will describe the project and its code structure. I would then love to hear your thoughts on this.

How does the application work?

The console application (Cloney) will do different things depending on the input arguments it is provided with. For instance, if I type

cloney --clone --source=c:/MyProject --target=d:/MyNewProject

Cloney will clone the solution according to certain rules.

To keep the design clean and flexible, I introduced the concept of sub routines. A sub routine is a class that implement the ISubRoutine interface, which means that it can be executed using the input arguments of the console application.

For instance, CloneRoutine listens for the input arguments above, and triggers if the correct arguments are provided. Another sub routine - HelpRoutine - will trigger if I type:

cloney -–help

When I run the application, Cloney fetches all ISubRoutine implementation then tries to execute each using the provided input arguments. Some may trigger, some may not. If no sub routine triggers, Cloney displays a help message.

So, what is the problem?

Well, there is really no problem…just different ways to do things.

For instance, when I parse and handle the input arguments, I use a class called CommandLineArgumentParser, which implements ICommandLineArgumentParser. This class transforms the default argument array to a dictionary and makes it easy to map an arg key to an arg value.

Each sub routine determines whether or not it wants to use this util. Since the sub routine interface only defines the following method:

bool Run(IEnumerable<string> args)

each sub routine is basically a program of its own. As far as the master program is concerned, it just delegates the raw argument array to each sub routine. Then, it’s up to each sub routine to use it as it sees fit.

The old design – DI for all (too much?)

Previously, the CloneRoutine class had two constructors:

public CloneRoutine()
: this(Default.Console, Default.Translator, Default.CommandLineArgumentParser, Default.SolutionCloner) { ... } 

public CloneRoutine(IConsole console, ITranslator translator, ICommandLineArgumentParser argumentParser, ISolutionCloner solutionCloner) { ... }

Since the sub routines are created using reflection, each routine must provide a default constructor. Here, the default constructor uses a default implementation of each interface, while the custom constructor is used by unit tests. Here, the dependencies are fully exposed and pluggable.

So, what is the problem?

Well, I just feel that since the input arguments are what defines what a routine should do, injecting an argument parser makes the class unreliable. If I provide the class with a parser that returns an invalid set of arguments, the class will not do what I expect. Isn’t that bad?

The new design – DI where I think it’s needed (enough?)

Instead of the old design, would it not be better to do things like this:

public CloneRoutine()
: this(Default.Console, Default.Translator, Default.SolutionCloner) { ... }

public CloneRoutine(IConsole console, ITranslator translator, ISolutionCloner solutionCloner) {
  ...
  this.argumentParser = Default.CommandLineArgumentParser;
  ...    }

This way, I depend on an ICommandLineArgumentParser implementation that is wired up in a Default class. If that implementation is incorrect, my unit tests will break. The other three injections (IMO) are the ones that should be exchangable.

Is this good design, or am I doing something bad by embedding a hard dependency, especially since all other component dependencies can be injected?

Update 2017-04-05

I guess you’re not supposed to look at what you wrote some years ago. I am sorry for wasting bytes on the Internet with these lines of code.