当许多相似但不同的类需要许多相似但不同的依赖项时,如何才能最好地实现依赖注入?

时间:2012-11-02 21:36:44

标签: .net dependency-injection mvp

我正在使用MVP模式开发Visual Studio .NET 4.0中的Windows应用程序,并尝试在整个应用程序中学习和应用依赖项注入原则。但我无法理解如何最好地处理特定情况。我提前为长篇解释道歉,但我不知道我是否能够具体到其他方面。

该应用程序是许多不同类型记录的通用记录保存应用程序,其中一些记录彼此相关,其中一些记录与其他记录无关。我们使用的每种不同记录类型构成了一个独特的特征"在申请中。因此,应用程序的启动屏幕本质上是一个交换机,他们可以从中选择要导航到的功能。

当他们进行选择时,该功能的窗口(视图)打开,并且从该窗口开始,他们可以执行与该功能相关的各种操作。大多数功能(例如,客户,订单)具有用户可以执行的一些常见操作(创建新记录,打开现有记录,保存更改,打印等)。然而,一些具有特定于该一个特征的操作(例如,特定于订单特征的验证操作)。我的问题在于弄清楚如何在每个功能中执行操作。

由于我使用MVP,因此功能窗口有一个演示者,当用户启动该功能的任何操作时,该演示者会响应。但是,由于我想遵循单一责任原则,我不希望演示者成为神级,实际上知道如何创建记录,保存,打印等等。所以我的想法是定义单独的类来处理每个操作,就像使用.Create()方法的ICreateOrders实现,带有.Save()方法的ISaveOrders实现一样。只是为了给它们命名,我会称它们为#34; resolvers& #34;因为他们实际上解决了该操作发生的请求。其中一些常见的解析器可能具有基本实现(例如基本FeatureSave类),因为许多代码在各个功能之间是相似的,但是每个功能都需要能够在需要时具有特定的实现。这种方法提出了几个问题。

首先,我希望能够从应用程序中的其他位置启动这些操作,而不仅仅是该功能的窗口。例如,我不仅能够在订单功能窗口中打开订单记录,而且还希望能够从客户功能中选择"查看订单"并自动打开并显示该客户的订单记录。为了实现代码重用,我想使用相同的IOpenOrders实现来完成工作,无论应用程序在哪里发起请求。因此,只需在Orders窗口的演示者中嵌入所需的解析器似乎不是一种选择。

所以我的想法是创建一个包含Feature对象集合的FeatureService类。在应用启动时,我从数据库中获取了对登录用户有效的所有功能的列表,然后我遍历该列表并为每个功能创建一个功能对象,并将其添加到FeatureService的集合。然后,当我需要在另一个功能中启动一个操作时,我可以通过将FeatureService对象注入源类,从中获取目标功能,然后使用它来执行操作来实现。但我也不希望Feature类成为God类,所以这只会将所有单独的解析器从Orders presenter移动到Feature类。

但是为了实现这一点,必须向Feature对象注入它可能需要的所有解析器,而不知道用户是否将执行这些操作。对于一些更复杂的功能,这可能是20或30个不同的解析器被注入,这当然看起来像构造函数注入滥用。虽然这使得Feature对象不会成为神级,因为它只是将每个操作都传递给它注入的解析器,这似乎是错误的。当一个物体成为一个“上帝 - 协调者”时,这是正确的用法吗?像这样?

我的另一个问题是关于这些物体的数量。对于某些客户,可能有50个或更多可用的单独功能(随着时间的推移,无疑会有更多功能)。因此,当我在启动时创建这些Feature对象并且我的容器将所有这些解析器依赖项注入到每个对象中时,我创建了数百个对象(例如,每个功能有40个特征X 20个解析器),我是否访问过一些这些功能与否。典型的用户会话可能只涉及一些功能,因此在启动时创建这么多对象似乎很浪费。我可以放弃并让Feature对象在内部执行ServiceLocation以获取其解析器,但如果有更好的方法,我想使用它。

我无法弄清楚从哪里开始。我试图查看并找到有关此问题的示例和信息,但我不知道我是否知道足以知道要搜索的内容。任何人都可以提供的指导或见解将不胜感激。

1 个答案:

答案 0 :(得分:2)

如果我理解正确,您尝试使用ICreateOrdersISaveOrders构建的内容是Repository pattern

通常,存储库可能如下所示:

public interface IRepository<TEntity>
{
    TEntity GetByKey(int key);

    TEntity CreateNew();

    void Save(TEntity entity);

    void Delete(TEntity entity);
}
  

但要实现这一点,必须使用all注入Feature对象   它可能需要的解析器,而不知道用户是否会执行   那些操作与否。对于一些比较复杂的功能   当然可以注入20或30个不同的旋转变压器   似乎是构造函数注入滥用。

有一种模式可以将存储库组合在一起。它被称为Unit of Work pattern

工作单元使用业务事务并协调写出更改,并允许访问它所拥有的存储库。

通常,当消费者需要许多存储库时,您会注入IUnitOfWork。这仍然只是将存储库注入工作单元的问题。这个问题可以通过将工作单元作为工厂实现,或者将存储库工厂注入工作单元来解决:

public interface IRepositoryFactory
{
     IRepository<TEntity> CreateRepository<TEntity>();
}

IRepositoryFactory创建实现是微不足道的,因为这会直接映射到您的DI容器:

private sealed class FrameworkSpecificRepositoryFactory
    : IRepositoryFactory
{
     private readonly Container container;

     public FrameworkSpecificRepositoryFactory(Container container)
     {
         this.container = container;
     }

     public IRepository<TEntity> CreateRepository<TEntity>()
     {
         return this.container.GetInstance<IRepository<TEntity>>();
     }
}

此实现直接使用DI容器。它看起来像Service Locator anti-pattern的一种形式,但这取决于此类的位置。如果此实现是Composition Root的一部分,那么此实现是infrastructural component,在这种情况下,它很好。诀窍是保持任何业务逻辑不受基础设施组件的影响。

P.S。尽量让你的问题下次缩短。更短的问题更有可能得到解答。