接口隔离原理用法

时间:2014-06-10 13:19:16

标签: c# convention interface-segregation-principle

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

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列表并添加对象(丑陋)或者第二种方法 - 创建一个列表并在可能(丑陋)的情况下投射它们吗?我不知道这样做的最佳惯例是什么。

这个例子可能不是我能想到的最好的例子,但我希望你知道我的意思。

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

3 个答案:

答案 0 :(得分:5)

如果你的系统中存在运行和吃掉的东西,那么将其定义为接口:

public interface IHungryRunner : IRunnable, IEatable
{ }

假设IRunnable和IEatable将永久使用,否则只需将它们保持在一起。

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

答案 1 :(得分:3)

软件架构中的每个规则,约定和设计模式都应该采取一些措施。您需要考虑为什么将某些内容视为规则/约定/设计模式,以便知道何时应用它。

根据Wikipedia

  

保持课程专注于单一问题的重要性   是因为它使课程更加健壮。继续[...生成报告并打印它...]示例,如果报告编译过程发生了变化,那么   如果它是其中一部分,则打印代码会破坏更大的危险   同一个班级。

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

在您的Connect() / Disconnect()示例中,我假设这两个操作的内聚性非常强大,如果一个更改,则很可能需要 >也要改变。因此,在一个界面中合并Connect()Disconnect()并不违反SRP。

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

最终:务实!您是否希望将Eat()Run()拆分为单独的界面,以便在可预见的未来提供任何好处?如果没有,您可能会违反其他设计原则:YAGNI

答案 2 :(得分:2)

保留你的IPerson界面是合理的,因为人们可能会说正在运行正在进食都是&#34;成为一个人的责任&#34 ;

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

public interface IPerson : IRunnable, IEatable {}

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

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