Constructor injection (DI) versus static plants for cross problems?

In most arbitrary applications, there are many cross-cut problems that need to be addressed among all available layers, for example. logging, message bus, configuration. I noticed that in some classes they tend to explode the constructor if modules are introduced using IoC.

public class MyService : IService { public MyService(ILogger logger, IAppSettings settings, IEventBus eventBus...) { } } 

For ordinary cases of constructor over injection, I tend to reflect problems in building blocks that are closely related to each other, so I get fewer dependencies in the class. However, this is not possible with cross-sectional concepts.

Among registration frameworks, static factories / services look very popular, like

 // Application root MyLoggerService.SetFactory(log4NetFactory); // Somewhere MyLoggerService.GetLogger("name") // returns Log4NetLogger created by Log4NetFactory. 

My question is: is this approach good for all kinds of cross-stuff? What are the disadvantages if the code might look like this:

 public class MyService : IService { private readonly IReallyNeedThat _dependency; public MyService(IReallyNeedThat dependency) { _dependency = dependency; } private readonly ILogger _logger = LoggerService.GetLogger("MyService"); private readonly IEventBus _eventBus = EventBusService.GetEventBus(); private readonly IConfiguration _configuration = ConfigurationService.GetConfiguration(Level.Roaming) private readonly IExceptionHandler _exceptionHandler = ExceptionPolicy.GetHandler(); private readonly ITracer _tracer = TraceManager.GetDebugTracer(); } 
+8
c # design-patterns dependency-injection cross-cutting-concerns factory
source share
2 answers

If you are more into TDD, you can easily guess which approach is better.

When injecting dependencies, your code becomes more (unit) . You can inject dependencies through some mocking structure and create your own unit tests without a big headache.

But in the case of static factories, since your factory classes are (rigidly) connected to your class, while unit testing has no way out, how can you introduce them from outside your class.

DI Benefits in Static Factories -

  • Parallel development . Think about what service you are using, which you are building by someone else, and you are going to unit test your code (and you don't care, unit testing the logging service, since you assume it should be checked on the module when you use it ) You use the DI test data, inject the dependency using the layout, and execute.

  • Speed . When testing your classes, you definitely don’t want them to take a lot of time (so that he gives you a coffee break with every change to your main class;)). You would definitely want your unit test to work instantly and report any errors. A static factory that depends on external resources (e.g. network / DB, FileSystem) will take some time. You better use DI, use the object layout, and do.

  • Testability - DI helps isolate the client from their dependencies (facilitates the use of interfaces), therefore improves testability (through the use of mocks).

+6
source share

Moving dependencies from the constructor does not solve the problem, because you do not reduce the number of dependencies that the class has, and the likelihood that you still violate the "Shared Responsibility Principle" and the "Open / Close" principle , making your code difficult to test, it is hard to change and hard to maintain.

Instead, often a good solution is to get these end-to-end problems out of your components and put them in components specifically designed for this end-to-end task, and that component wraps the original component. In other words: create decorators .

This probably forces you to change the design of your classes, because when you do not have common abstractions to define sets of related services, you will need to define a decorator for abstraction and this will cause a lot of code duplication, which is bad in all cases.

So instead, simulate your system around command / handlers and query / handlers , and you will find yourself in a much better place. You can decorate every piece of business logic with a universal decorator that you define once and reuse everywhere. This allows your system to be clean, but still very flexible.

+5
source share

All Articles