2

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

接口隔离原则是为了防止出现某些接口实现不使用它的功能的情况——这是显而易见的。经常有这样的情况,我有一个接口列表,我想用它们做点什么。让我们看一个没有 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 列表并添加对象(丑陋)还是第二种方法 - 创建一个列表并在可能的情况下投射它们(丑陋)?我不知道这样做的最佳惯例是什么。

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

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

4

3 回答 3

5

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

public interface IHungryRunner : IRunnable, IEatable
{ }

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

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

于 2014-06-10T13:25:24.490 回答
3

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

根据维基百科

让类专注于单个关注点很重要的原因是它使类更加健壮。继续 [...生成报告并打印...] 示例,如果报告编译过程发生更改,如果打印代码属于同一类,则存在更大的危险,即打印代码将中断。

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

在您的Connect()/Disconnect()示例中,我会假设这两个操作的凝聚力非常好,以至于如果一个操作发生变化,则另一个操作很可能也需要更改。因此,在一个接口中组合Connect()Disconnect()不会违反 SRP。

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

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

于 2014-06-10T13:37:18.363 回答
2

保留您的 IPerson 界面是合理的,因为有人可能会争辩说,跑步吃饭都是“做人”的责任。

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

public interface IPerson : IRunnable, IEatable {}

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

void DoSomethingWithRunnables(IEnumerable<IRunnable> runnables)
{
    ...
    foreach (var item in runnables)
    {
        item.Run();
    }
    ...
}
于 2014-06-10T13:48:35.707 回答