【问题标题】:Dependency injection & constructor madness revised依赖注入和构造函数疯狂修正
【发布时间】:2014-07-18 01:50:57
【问题描述】:

阅读这个问题How to avoid Dependency Injection constructor madness? 后,我仍然对我的应用程序设计有些顾虑。假设我有一个类在其构造函数中接受很少的参数:

public class SampleViewModel
{
    public SampleViewModel(IReader1 reader1, IReader2 reader2, IReader3 reader3)
    {
        // ...
    }
}

IReaderX 是一个用于从不同来源检索数据的接口,如下所示:

public interface IReader1
{
    int Value1 { get; }

    string Value2 { get; }
}

现在,如果我想将这些接口聚合为一个,我将不得不创建另一个类,比如ReaderManager,它将充当底层类属性的包装器。很多管道代码。不好,如果你问我。

我尝试使用 Composition 并将所有阅读器作为 ReaderManager 类中的属性,但如果我试图在外部使用这些阅读器,则会违反得墨忒耳定律。

所以问题是:我应该如何减少不相互通信并且只公开属性而不是内部逻辑的构造函数依赖项的数量?

【问题讨论】:

  • 您的视图模型能否拆分为需要较少参数的较小视图模型?
  • 并非如此。这是核心视图模型,它从应用程序开始并管理其他窗口。它必须使用来自所有阅读器的验证数据,并在此基础上更改视图的状态。
  • 是不是因为不同的数据源的接口也不同,难道不能有一个接口被所有数据源实现,这样就可以用一个接口的List作为依赖?
  • 每个接口从不同的源读取不同的数据。它们有很多属性,将它们绑定在一起最终会形成巨大的类,这也违反了 SRP。
  • 很难说,因为您发布的代码非常通用,并没有透露 ViewModel 的职责。但是,如果您的读者非常不同,那么 Ned Stoyanov 考虑拆分 ViewModel 的建议听起来是正确的。甚至您对它的描述听起来都像是有太多职责(启动应用程序、管理窗口、加载数据、验证、更改视图状态)。

标签: c# design-patterns dependency-injection


【解决方案1】:

从几个不同的角度来看待它:消费者和更高层次的设计。


从`SampleViewModel`的角度来看

不喜欢有这么多合作者吗?也许如果它有它的druthers,它就只有一个合作者。那个合作者会是什么样子?为它创建一个interface to represent the role

例如:

public interface ISampleViewModelReader
{
    int Value1    { get; }
    string Value2 { get; }

    double Value3 { get; }

    string Value4 { get; }
}

public class AggregatedSampleViewModelReader : ISampleViewModelReader
{
    public AggregatedSampleViewModelReader(IReader1 reader1, IReader2 reader2, IReader3 reader3)
    {
        // ...
    }
    // ...
    double Value3 { get { return reader2.Value3; } }
    // ...
}

public class SampleViewModel
{
    public SampleViewModel(ISampleViewModelReader reader)
    {
        // ...
    }
}

您表示您对这种方法有疑虑,因为它会涉及“大量管道代码”。但是考虑到这个管道代码将存在或不存在包装类。通过定义一个包装类,至少您确定了一个对象,其唯一职责是处理此管道,而不是将其混入SampleViewModel 的其他职责中。


从更高层次的设计角度

其他对象如何使用IReaderX 对象? IReader1IReader2IReader3经常一起使用吗? IReader1IReader3 怎么样?

问这个问题的目的是识别“隐藏”的抽象,以便使它们更加明确。如果某些对象经常串联使用,它通常代表更广泛的设计理念。

但有时a rose is a rose is a rose。也许SampleViewModel 是唯一使用IReaderX 对象的东西。或许SampleViewModel 的唯一职责是聚合各个读者。在这些类型的情况下,拥有多个合作者并没有错。

如果稍后添加了另一个协作者(例如,IReader4),那么所有这些评估都应该重新进行。有时,设计只是碰巧在你身上跳出来。

【讨论】:

  • 让我想起了 mark seemann 博客中关于泄漏抽象的博文:blog.ploeh.dk/2010/12/02/Interfacesarenotabstractions,这也是:blog.ploeh.dk/2010/12/03/Towardsbetterabstractions
  • 问题是所有IReaderX 实例既可以单独使用也可以一起使用,具体取决于消费者。似乎没有一个好的解决方案。如果我不想为特定属性创建许多小的包装器,那么我必须创建一个大的包装器类,这反过来又违反了 SRP。通过管道代码,我的意思是多个属性包装器,我们可以通过显式使用读取器来避免这种情况。我必须使用现有架构来设计我的应用程序部分,也许问题在于更高级别的系统设计。
  • @MarekKowalczyk 然后几乎可以肯定有一些抽象还没有被识别出来。 IReaderX 对象被用作达到某种目的的手段。也就是说,它们是某事“什么”的“如何”。 (并且由于不同的事物以不同的方式使用它们,因此每种情况都可能代表识别抽象的机会。)可以帮助识别抽象的问题可能是:“在某物从IReader1 获得Value1 之后,它有什么用?”
  • @Lilshieste 好吧,也许我们应该考虑真实的例子。我有三个阅读器,我在我的视图模型中使用它们来改变它的视觉状态,即视图。但首先我需要执行验证,检查所有阅读器中的数据是否正确加载。这不是一个微不足道的逻辑,所以我需要另一个人来完成这项工作,比如ReaderValidator。它有事件DataValidated,在异步验证后触发。然而我仍然明确需要这些读者。您将如何在视图模型中编写这样的逻辑?鉴于 ofc,我们需要避免构造函数的疯狂并遵守 SRP。
  • @MarekKowalczyk 目前谁在调用ReaderValidator?视图模型?
猜你喜欢
  • 2011-01-26
  • 1970-01-01
  • 2011-02-02
  • 2011-12-29
  • 1970-01-01
  • 2019-04-20
相关资源
最近更新 更多