Dependency Injection gone too far?

I am currently working with a new version of a hobby console app of mine, that should execute certain actions depending on the input arguments. Now, I wonder if I am taking the concept of dependency injection too far.

Which dependencies should you inject, and which should you keep as invisible dependencies?

How does Cloney work?

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

 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, the CloneRoutine listens for the input arguments above, while the HelpRoutine triggers on cloney –help.

When I start the application, Cloney fetches all ISubRoutine implementation and tries to execute each with the input arguments. Some may trigger, some may not. If no 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 it comes to parsing the input arguments and making them convenient to handle, I use a class call CommandLineArgumentParser, which implements ICommandLineArgumentParser. The class transforms the default string array to a dictionary, which makes it easy to map an arg key to an arg value.

Using the class is a choice each sub routine must take. The interface just defines the following method:

 bool Run(IEnumerable<string> args)

Yeah, that’s right. Each sub routine just acts like a program of its own. As far as the master program is concerned, it just delegates the raw argument array it receives to each sub routine. How the routine handles the arguments is entirely up to it.

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

Previously, the CloneRoutine 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 a sub routine is created with reflection, it must provide a default constructor. Here, the default constructor use default implementations of each interface, while the custom constructor is used in the unit tests and supports full dependency injection. Each dependency is exposed and pluggable.

So, what is the problem?

Well, I just feel that since the command line arguments are what defines what the routine should do, letting the class behavior be entirely determined by how another component parses the input arguments, makes the class unreliable.

If I provide the class with an implementation that returns an invalid set of arguments, even if I provide the routine with arguments that it should trigger on (and especially considering that the return value is a do-as-you-please IDictionary), the class may explode due to an invalid implementation.

It that not bad?

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

Instead of the old design, it this not better:

   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 the choice of ICommandLineArgumentParser implementation that I have made in the Default class, but if that implementation is incorrect, my unit tests will break. The other three injections (IMO) are the ones that should be exchangable. The argument parser should not be.

Is this good design, or am I doing something terribly bad by embedding a hard dependency, especially since all other component dependencies can be injected. Please provide me with your comments regarding this situation.

Advertisements