Is Service Locator easier to use than Dependency Injection?

The application I'm working on relies on Autofac as a DI container, and one of the reasons I decided to use it, among others, was the delegate function of the factory (see here )

This is great for all cases when I need to recreate the same elements several times, as is the case with some reports and related screens. Some reports (even the same type) are executed at the same time, but they only change according to their user parameters, so it makes sense (I think) to introduce factories to create instances, transfer free parameters and leave the rest of the application.

The problem is that each report is composed of a variable number of supporting reports (tasks), and each task implements the ITask interface. Each report can contain up to 50 different tasks, and each task encapsulates an exact business operation. One of the options that I have is to implement delegate factories and create them when necessary.

These tasks should be dynamically generated in factories and something like:

var myTaskA = _taskFactoryConcreteTaskA(); var myTaskB = _taskFactoryConcreteTaskB(); var myTaskC = _taskFactoryConcreteTaskC(); ... var myTaskZZ = = _taskFactoryConcreteTaskZZ(); 

it takes a lot of manual wiring (delegates, constructor, support fields, etc.), and something like

 var myTaskA = _taskFactory.Create<ConcreteTaskA>(); var myTaskB = _taskFactory.Create<ConcreteTaskB>(); var myTaskC = _taskFactory.Create<ConcreteTaskC>(); ... var myTaskZZ = _taskFactory.Create<ConcreteTaskZZ>(); 

it would be incredibly less work, especially if _taskFactory wraps the container, as shown in this other post , but also basically means that I use the service locator to create my tasks.

What other options do I have that might be useful to you?

(NOTE: there is a good chance that I am completely unaware, and that I have to read a lot more about DI, in which case any input will be even more important)

+6
source share
2 answers

One approach worth exploring is to break the problem down into “work jobs” that use a set of related tasks:

 public class WorkItem1 : ISomeWork { public WorkItem1(Task1 t1, Task2 t2...) { } public void DoSomething() { ... } } 

Then your use of plants would decrease by someWorkFactory().DoSomething() , perhaps by several different kinds of "something."

A class that has a large number of dependencies in factories or something else usually indicates that there are smaller, more focused classes waiting to be discovered in order to break the job.

Hope this helps.

+3
source

Since the factories mentioned in the question do not accept any arguments using the smells of the factory Leaky Abstraction . As Nicholas Bloomhardt points out in his answer, the best approach could be to simply introduce every task into the consumer.

In this case, since all tasks implement the same interface, instead of introducing up to 50 different instances of ITask , you can compile them:

 public class MyConsumer { private readonly IEnumerable<ITask> tasks; public MyConsumer(IEnumerable<ITask> tasks) { this.tasks = tasks; } public void DoSomething() { foreach (var t in this.tasks) { // Do something with each t } } } 

Alternatively, you can compose an ITasks sequence in Composite , which is actually my preferred solution:

 public CompositeTask : ITask { private readonly IEnumerable<ITask> tasks; public CompositeTask(IEnumerable<ITask> tasks) { this.tasks = tasks; } // Implement ITask by iterating over this.tasks } 

This will simplify the user and turn the fact that in the implementation details several tasks are necessary:

 public class MyConsumer { private readonly ITask task; public MyConsumer(ITask task) { this.task = task; } public void DoSomething() { // Do something with this.task } } 
+6
source

Source: https://habr.com/ru/post/901240/


All Articles