【问题标题】:Interface segregation principle usage接口隔离原理用法
【发布时间】:2014-06-10 13:19:15
【问题描述】:

这种情况发生在我身上很多次,我不知道如何解决。

Interface segregation principle 是为了防止出现某些接口实现不使用它的功能的情况——这很明显。经常有这样的情况,我有一个接口列表,我想用它们做点什么。让我们看看没有 ISP 的例子:

  public interface IPerson
    {
           void Run();
           void Eat();
    }

public class AbcPerson : IPerson
{
       void Run(){};
       void Eat(){};
}

public class XyzPerson : IPerson
{
       void Run(){};
       void Eat(){};
}

List<IPerson> People {get;set;}

我想和每个人一起跑步。

      foreach(var person in People)
      {
         person.Run();
      }

现在我想和他们一起吃饭。

          foreach(var person in People)
          {
             person.Eat();
          }

现在如果我想使用 ISP,我应该将代码更改为:

        public interface IRunnable
        {
               void Run();
        }
        public interface IEatable
        {
               void Eat();
        }

    public class AbcPerson : IRunnable,IEatable
    {
           void Run(){};
           void Eat(){};
    }

    public class XyzPerson : IRunnable,IEatable
    {
           void Run(){};
           void Eat(){};
    }

我应该如何制作我的人员名单?我应该制作两个 Runable 和 Eatable 列表并添加对象(丑陋)还是第二种方法 - 创建一个列表并在可能的情况下投射它们(丑陋)?我不知道这样做的最佳惯例是什么。

这个例子可能不是我能想象的最好的例子,但我希望你明白我的意思。

已编辑:我更改了接口和类以及原理名称。

【问题讨论】:

  • 我不认为在同一个界面中有Connect 和Disconnect 违反了SRP。您应该将您的界面描述为比IConnect 更通用的东西,也许可以描述您的产品,例如IDevice,它们可以一起生活
  • 是的,这就是为什么我写它不是一个完美的例子。它应该是不同的东西,比如 Eat() 和 Run(),我会编辑它
  • SRP 是“做一件事”的原则,这里我并没有真正看到与接口的链接。它也与 ISP 没有真正的关系,因为您正在使用所有方法。我会说这更像是一个 OCP 案例,但这实际上取决于系统稍后会如何改变,我们不知道。
  • 是的,你完全正确。我拼错了名字。我的意思是接口隔离原则
  • 您可以添加第三个接口来继承两者。

标签: c# convention interface-segregation-principle


【解决方案1】:

如果你的系统中有一个东西可以运行并吃掉,那么将它定义为一个接口:

public interface IHungryRunner : IRunnable, IEatable
{ }

假设 IRunnable 和 IEatable 将永远单独使用,否则将它们放在一起。

您还可以维护单独的 IRunnables 和 IEatables 列表,而不是试图将它们混为一谈。

【讨论】:

    【解决方案2】:

    软件架构中的每条规则、约定和设计模式都应该谨慎对待。您需要考虑为什么某些东西被视为规则/约定/设计模式,以便知道何时应用它。

    根据Wikipedia:

    让课程专注于单一关注点很重要的原因 是它使类更健壮。继续 [...生成报告并打印...] 示例,如果报告编译过程发生变化,则 如果打印代码是 同一个班级。

    这里的重点是生成报告的过程本质上独立于打印报告的过程。因此,应该允许这些操作单独更改。

    在您的 Connect() / Disconnect() 示例中,我假设这两个操作的内聚性非常好,以至于如果一个操作发生变化,那么另一个操作需要更改为好。因此,将Connect() 和Disconnect() 组合在一个界面中不会违反SRP。

    另一方面,如果操作是Eat()和Run(),则需要考虑这两个操作的内聚性。

    最后:务实!您是否希望将Eat() 和Run() 拆分为单独的接口以在可预见的未来提供任何好处?否则,您可能会违反另一个设计原则:YAGNI。

    【讨论】:

    • 此外,使用成员直接从 IPerson 重构为 IPerson : IEatable, IRunnable 是无痛的,因为引用 IPerson 的任何类都不必更改
    【解决方案3】:

    保留您的 IPerson 界面是合理的,因为有人可能会争辩说 running 和 eating 都是“成为一个人”的责任。

    如果您觉得定义单独的 IRunnable 和 IEatable 接口很有价值,那么您可以使用它们来定义您的 IPerson 接口:

    public interface IPerson : IRunnable, IEatable {}
    

    这有点偏离问题的单一责任方面,但我感觉到这可能是困境的一部分:如果您有其他代码只关心事物的“可运行”方面,那么您可以有一个采用IEnumerable&lt;IRunnable&gt; 参数的方法。通过 .NET 4 中引入的协方差规则,您的 List&lt;IPerson&gt; 对象是 IEnumerable&lt;IRunnable&gt;,因此您可以简单地传递它而无需任何转换。

    void DoSomethingWithRunnables(IEnumerable<IRunnable> runnables)
    {
        ...
        foreach (var item in runnables)
        {
            item.Run();
        }
        ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 2016-10-19
      • 1970-01-01
      • 2022-11-09
      • 2021-09-28
      • 1970-01-01
      相关资源
      最近更新 更多