【问题标题】:How strict should be interfaces (a good interface design)接口应该有多严格(一个好的接口设计)
【发布时间】:2014-05-16 14:35:28
【问题描述】:

我不确定这是否适合 StackOverflow,或者更适合 Programmers@StackExchange。如果这应该去那里,请在下面的评论中告诉我,我会移动它:)

无论如何 - 回到正题。我从来没有使用接口和构造函数/属性依赖注入等进行太多编程。所以我对它了解得太多了。不过,我一直在阅读一些文章,主要是 this,并发现这是一种有趣的技术,可以使我的软件更加灵活和可测试。
所以我开始重构一个现有的应用程序 (C#),我遇到了一个难题,以下 2 个选择中的一个更好:
选择 1 - 函数中的最低依赖要求.给构造函数留一些注入(使用接口时的实现决策)

public interface IDriver
{
    bool Start();
    bool Stop();
    bool Read(uint[] signal1, uint[] signal2);
}

public class MyDriver : IDriver
{
    public MyDriver(ISettings settings)
    {
        //remember ISettings in a local var
    }

    //interface implementation
}

选择 2 - 函数调用中的所有必需依赖项。

public interface IDriver
{
    bool Start();
    bool Stop();
    bool Read(ISettings settings, uint[] signal1, uint[] signal2);
}

public class MyDriver : IDriver
{
    //implementation of the interface
}

现在选择 2 可能是错误的,对吧?因为某些实现实际上可能不需要 ISettings 来工作。我的 IDriver 实现目前使用 ISettings 的事实并不意味着它会在一年左右的时间内使用,因此合乎逻辑的方法是使用方法 1。

所以我的问题是:我应该使我的接口有多严格,以及如何在接口和实现之间不混淆?我不希望实现影响我设计界面的方式。
另外,有人知道有关该主题的好文章吗?

谢谢。

【问题讨论】:

  • +1 用于回复答案,但 -1 您需要为有帮助的答案投票(有很多原因),来吧!

标签: c# interface dependency-injection


【解决方案1】:

接口应该由使用接口的客户端定义和拥有。正如Agile Principles, Patterns, and Practices 解释的那样,“客户 […] 拥有抽象接口”(第 11 章)。因此,如果客户只需要这个工作(你的选择1):

public interface IDriver
{
    bool Start();
    bool Stop();
    bool Read(uint[] signal1, uint[] signal2);
}

那应该是界面。其他一切都是实现细节,应该放在构造函数中。

【讨论】:

  • 感谢这本书的链接,我可能真的会买它:)
【解决方案2】:

对系统要求的深入研究可以解决许多问题,并帮助您更有信心地设计应用程序。所以首先,要多思考,直到你可以争论和推理你将要做什么。

其次,恕我直言,这两种方法都可以。 'raja' 指出的第一个是干净和成功的,我不再重复他说的话。但是考虑一下这种情况:如果稍后需要配置 IDriver 实现者。然后将某种设置传递给他们可以解决许多问题。即使目前您认为没有必要(我承认这是YAGNI原则所说的),您也可以提供空设置(NullObject pattern):

public Driver : IDriver
{
   public bool Read(ISettings settings, uint[] signal1, uint[] signal2)
   {
      if (settings.PreventSomeThing)
      {
         .....
      }

   }
}

public NullSetting : ISetting
{
   public bool PreventSomething = false;   
   ....
}

【讨论】:

    【解决方案3】:

    不仅仅是严格,这是一个合同必要性的问题。

    您的读取功能是否需要 ISettings?可能没有。

    认为它与 signal1 和 signal2 变量没有什么不同。在接口的 Read 方法定义中有 signal1 和 signal2 的原因是因为它们是合同的一部分,并且对于接口的每个实现都必须用作输入。

    但 ISettings 听起来像是特定实现需要而其他一些实现不需要的东西。 (如 Loggers、CacheManagers、Repositories 等)

    所以你是对的,而且通常情况下,方法 #1 会更可取。它使界面保持清洁并仅限于精确的合同输入/输出。

    【讨论】:

    • 感谢您的输入,这就是我在考虑的问题 - 如果我使用接口,这是减少耦合的其他原因。感谢您的意见。
    猜你喜欢
    • 1970-01-01
    • 2010-12-13
    • 2012-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多