C# 界面分离原理及应用

C# 界面分离原理及应用,c#,convention,interface-segregation-principle,C#,Convention,Interface Segregation Principle,这种情况发生在我身上很多次,我不知道如何解决它 这是为了防止出现一些情况,当一些接口实现不使用它的功能时——这是显而易见的。通常情况下,我有一个接口列表,我想用它们做点什么。让我们看一个没有ISP的例子: public interface IPerson { void Run(); void Eat(); } public class AbcPerson : IPerson { void Run(){};

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

这是为了防止出现一些情况,当一些接口实现不使用它的功能时——这是显而易见的。通常情况下,我有一个接口列表,我想用它们做点什么。让我们看一个没有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.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(){};
    }
我应该如何列出我的名单?我是否应该列出两个可运行和可吃的列表,并添加对象(丑陋),或者第二种方法-创建一个列表,如果可能的话,将它们强制转换(丑陋)?我不知道这样做的最佳惯例是什么

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


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

如果您的系统中有运行和使用的东西,请将其定义为接口:

public interface IHungryRunner : IRunnable, IEatable
{ }
public interface IPerson : IRunnable, IEatable {}
假设IRunnable和IEatable将单独使用,否则就将它们放在一起


您还可以维护iRunTables和iDataTables的单独列表,而不是试图将它们放在一起。

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

根据:

让一个班级专注于一个问题是很重要的 它使类更加健壮。继续[…生成报告并打印…]示例,如果报告编译过程发生更改,则 如果打印代码是系统的一部分,则其损坏的危险更大 同一个班级

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

在您的
Connect()
/
Disconnect()
示例中,我假设这两个操作的内聚性非常强,如果其中一个操作发生更改,那么另一个操作很可能也需要更改。因此,在一个接口中组合
Connect()
Disconnect()
不会违反SRP

另一方面,如果操作是“代码> e>())/>代码> <代码> Run()>代码>,则需要考虑这两个操作的衔接。


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

保留IPerson界面是合理的,因为有人可能会认为跑步和吃饭都是“做人”的责任

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

public interface IHungryRunner : IRunnable, IEatable
{ }
public interface IPerson : IRunnable, IEatable {}
这有点脱离了问题的单一责任方面,但我感觉这可能是进退两难的一部分:如果您有其他代码只关心事物的“可运行”方面,那么您可以有一个接受
IEnumerable
参数的方法。通过.NET 4中引入的协方差规则,您的
列表
对象是一个
IEnumerable
,因此您可以简单地传递它而无需任何强制转换

void DoSomethingWithRunnables(IEnumerable<IRunnable> runnables)
{
    ...
    foreach (var item in runnables)
    {
        item.Run();
    }
    ...
}
void DoSomethingWithRunnables(IEnumerable runnables)
{
...
foreach(可运行项中的变量项)
{
item.Run();
}
...
}

我不认为在同一个接口中使用
连接
断开
会违反SRP。你应该把你的界面描述成比
IConnect
更一般的东西,也许可以描述你的产品,比如
IDevice
,它们可以一起生活在一起是的,这就是为什么我写这不是一个完美的例子。它应该是非常不同的东西,比如Eat()和Run(),我将编辑它的RP是“只做一件事”的原则,我在这里看不到接口的链接。它与ISP也没有太大关系,因为您使用的是所有的方法。。我想说这更像是一个OCP案例,但这实际上取决于系统以后可能会如何变化,我们不知道。是的,你完全正确。我把名字拼错了。我的意思是接口分离原则您可以添加第三个接口来继承这两个接口。此外,将成员直接从
IPerson
重构为
IPerson:IEatable,IRunnable
,这是毫无痛苦的,因为引用
IPerson
的类都不需要更改