服务定位器比依赖注入更容易使用

本文关键字:易使用 注入 依赖 定位器 服务 | 更新日期: 2023-09-27 18:14:25

我正在开发的应用程序依赖于Autofac作为DI容器,使我决定使用它的原因之一是委托工厂特性(见这里)

对于我需要多次重新创建相同元素的所有情况都可以正常工作,例如一些报告和相关屏幕的情况。有些报告(甚至是相同类型的报告)是并发执行的,但它们只通过用户定义的参数来改变,所以(我认为)注入工厂来创建实例是有意义的,传递自由参数,剩下的留给应用程序。

问题在于,每个报表都由数量可变的子报表(任务)组成,每个任务实现一个ITask接口。每个报表可以使用多达50个不同的任务,每个任务封装一个精确的业务操作。我的一个选择是为它们注入委托工厂,并在需要时创建它们。

这些任务必须由工厂动态生成,例如:

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

需要大量的手工连接(委托、构造函数、后台字段等),而像

这样的东西
var myTaskA = _taskFactory.Create<ConcreteTaskA>();
var myTaskB = _taskFactory.Create<ConcreteTaskB>();
var myTaskC = _taskFactory.Create<ConcreteTaskC>();
...
var myTaskZZ = _taskFactory.Create<ConcreteTaskZZ>();

将是令人难以置信的更少的工作,特别是如果_taskFactory包装容器,如这篇文章所示,但它也基本上意味着我使用一个服务定位器来创建我的任务。

我有什么其他的选择可能适合解决这个问题?

(注意:很有可能我完全偏离了轨道,我必须阅读更多关于DI的内容,在这种情况下,任何贡献都将更加重要)

服务定位器比依赖注入更容易使用

由于问题中指示的工厂不接受任何参数,因此使用工厂闻起来有抽象泄漏的味道。正如Nicholas Blumhardt在他的回答中指出的那样,一个更好的方法可能是简单地将每个任务注入到消费者中。

在这种情况下,由于所有任务实现相同的接口,而不是注入多达50个不同的ITask实例,您可以组合它们:

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
        }
    }
}

或者,您可以将ITasks序列组合成Composite,这实际上是我首选的解决方案:

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

这将简化使用者,并将有多个任务要执行的事实转化为实现细节:

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

一种值得研究的方法是将问题分解为使用一组相关任务的"工作单元":

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

然后,你对工厂的使用将下降到someWorkFactory().DoSomething(),可能是为了几种不同的"东西"。

如果一个类有大量的依赖项,比如依赖于工厂或其他任何东西,通常意味着有更小、更集中的类等着被发现来分解工作。