3

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

该应用程序是用于许多不同类型记录的通用记录保存应用程序,其中一些彼此相关,而另一些则与其他记录无关。我们使用的每种不同的记录类型在应用程序中构成了一个独特的“功能”。因此,应用程序的启动屏幕本质上是一个开关板,他们可以从中选择他们想要导航到的功能。

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

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

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

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

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

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

我不知道从这里去哪里。我试图查找并找到这方面的示例和信息,但我不知道我是否知道足以知道要搜索什么。任何人都可以提供的任何指导或见解将不胜感激。

4

1 回答 1

2

如果我理解正确,您尝试使用ICreateOrdersand构建的ISaveOrdersRepository 模式

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

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

    TEntity CreateNew();

    void Save(TEntity entity);

    void Delete(TEntity entity);
}

但要做到这一点,Feature 对象必须注入它可能需要的所有解析器,而不知道用户是否会执行这些操作。对于一些更复杂的功能,这可能是 20 或 30 个不同的解析器被注入,这看起来像是构造函数注入滥用。

有一种将存储库组合在一起的模式。它被称为工作单元模式

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

通常,当消费者需要许多存储库时,您会注入一个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 反模式的一种形式,但这取决于此类所在的位置。如果此实现是您的Composition Root的一部分,在这种情况下,此实现是一个基础设施组件,在这种情况下它很好。诀窍是将任何业务逻辑排除在您的基础设施组件之外。

ps下次尽量缩短你的问题。较短的问题更有可能得到回答。

于 2012-11-03T10:37:02.373 回答