这种情况多次发生在我身上,我不知道如何解决它。
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列表并添加对象(丑陋)或者第二种方法 - 创建一个列表并在可能(丑陋)的情况下投射它们吗?我不知道这样做的最佳惯例是什么。
这个例子可能不是我能想到的最好的例子,但我希望你知道我的意思。
编辑:我更改了接口和类以及原则名称。
答案 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();
}
...
}