Alex R.
Alex R.

Reputation: 684

SRP, DI and app.config: when to instantiate injecting classes?

I'm refactoring a small report-generating application and making it adhere to the SOLID principles, etc. So, all my classes follow SRP with DI and I use app.config for most parameter variations. I'm not using any DI frameworks, yet, but rather creating all the dependency classes at the app entry point. However, that led to a design question, which I'm generalizing here:

I can create my dependencies and main reporter classes like this:

//dependencies    
var sharedDependency = new SharedDependency();

var whiteColorDependency = new ColorDependency("white");
var blueColorDependency = new ColorDependency("blue");

var config1Dependency = new MultiDependency("config1");
var config2Dependency = new MultiDependency("config2");
...
var config12Dependency = new MultiDependency("config12");



//main logic
var reporter1 = new Reporter(sharedDependency, whiteColorDependency, config1Dependency);
var reporter2 = new Reporter(sharedDependency, whiteColorDependency, config2Dependency);
var reporter3 = new Reporter(sharedDependency, whiteColorDependency, config3Dependency);
...
var reporter10 = new Reporter(sharedDependency, blueColorDependency, config10Dependency);
var reporter11 = new Reporter(sharedDependency, blueColorDependency, config11Dependency);
var reporter12 = new Reporter(sharedDependency, blueColorDependency, config12Dependency);

or like that:

foreach (var config in configs)
{
    //dependencies
    var sharedDependency = new SharedDependency();
    var colorDependency = new ColorDependency("color");
    var configDependency = new MultiDependency("config");

    //main logic
    var reporter = new Reporter(sharedDependency, colorDependency, configDependency);
    reporter.DoSomething();
}

?

(The "colors" and "config" values are all coming from the app.config file and not hard-coded. The above is generalization, as I said, and the real project has more dependencies, dependencies of dependencies, with some more shared than the others.)

The first approach is more efficient, because the CSharedDependency is created only once and CColorDependency only twice. (It's also more readable to me.) The second is fully configuration-driven, though, thus requiring zero maintenance and being fully extensible.

So, which one is the best design?

Upvotes: 0

Views: 122

Answers (2)

Mark Seemann
Mark Seemann

Reputation: 233135

As far as I understand this question, the concern is that in the second alternative, SharedDependency is created multiple times, and that the ColorDependency instances, too, are created more times than necessary.

My first reaction to that is that it probably doesn't matter. If you follow the rule that constructors should do no work, creating a few objects more than necessary is most likely something that you're not going to notice - particularly if those objects subsequently engage in I/O. Object creation in .NET is fast (and I/O is slow).

That said, the 'issue' is probably easy to address.

If you wish to be fully configuration-driven, you'll need a way to distinguish between various groups of configuration values. The simplest way is most likely to prepend the appSettings keys with well-known prefixes, but a more robust approach would be to define custom configuration sections.

In any case, I'll assume that you can pull two collections out of your configuration system: colors and configs.

This should make it easy to create the Services in a configuration-driven manner:

var sharedDependency = new SharedDependency();
foreach (var color in colors)
{
    var colorDependency = new ColorDependency(color);

    foreach (var config in configs)
    {
        var configDependency = new MultiDependency(config);

        //main logic
        var reporter = new Reporter(sharedDependency, colorDependency, configDependency);
        reporter.DoSomething();
    }
}

All that said, before you start reinventing the wheel: most DI Containers support configuration via app.config, so that may also be an option for you.

And again: that said, consider whether using a DI Container is an appropriate choice after all. With text-based configuration, you lose the benefit of compile-time checking. You can also easily end up making the configuration system so complicated that code would be easier.

Unless you have a compelling reason to configure object graphs in configuration files, favour Pure DI.

Upvotes: 3

NightOwl888
NightOwl888

Reputation: 56849

The correct way to organize your application for dependency injection (whether using a container or pure DI) is to use a Composition Root at the application's startup to compose your application's object graph.

Therefore, your first example looks more in line with that because you would never access your container to create dependencies at runtime, but instead inject abstract factories to create those instances.

That said, it is unclear from your example how you have organized your application. Without having a composition root you are not doing dependency injection correctly. And since you had to ask this question, I suspect you don't have a composition root because your second example would never work if you did.

References:

https://www.kenneth-truyers.net/2014/11/18/how-to-use-pure-di/

Upvotes: 3

Related Questions