今天有人告诉我,C#中的接口实现只是“Can-Do”关系,而不是“Is-A”关系。这与我长期相信LSP(Liskov Substitution Principle)相冲突。我一直认为所有的继承都应该意味着“Is-A”的关系。
所以,如果接口实现只是一个“可以做”的关系。如果有一个界面“IHuman”和“IEngineer”,并且一个类“Programmer”继承自“IHuman”& “IEngineer”?当然,“程序员”是“IHuman”和“IEngineer”。
如果它只是“Can-Do”关系,是否意味着我们不能指望“程序员”实例行为在被视为IHuman并被视为IEngineer时可能会有所不同?
答案 0 :(得分:15)
根据我的经验,想到“is-a”和“can-do”关系并没有多大帮助。你很快就会遇到问题。它基本上是现实世界和OO之间的阻抗不匹配。然而,实际上很多人都在谈论对现实世界进行建模,您从根本上需要了解类型之间的关系在您正在使用的平台上的意义。
有时接口可以用作功能,有时它们可以代表更多正常的“is-a”关系。我不会太在意它 - 只要确保你明白他们能做什么,不能做什么。
答案 1 :(得分:5)
我倾向于将接口视为行为契约。诸如IComparable和IEnumerable之类的接口是经典示例。
在你给出的例子中,IHuman和IEngineer并不是真正的行为。
答案 2 :(得分:4)
发现这个问题的时间相当晚,但我想加入。
C#中的接口具有is-a关系,但不是is-an-object。相反,是实施。
换句话说,对于实现IBar的类Foo,进行以下测试:
Foo myFoo = new Foo();
return myFoo is IBar;
字面上返回true。你也可以说,
IBar bar = myArrayList[3] as IBar;
Foo foo = bar as Foo;
或者,如果方法需要IBar,则可以传递Foo。
void DoSomething(IBar bar) {
}
static void Main() {
Foo myFoo = new Foo();
DoSomething(myFoo);
}
显然,IBar本身没有实现,因此can-do关系也适用。
interface IBar { void happy(); }
class Foo : IBar
{
void happy()
{
Console.Write("OH MAN I AM SO HAPPY!");
}
}
class Program
{
static void Main()
{
IBar myBar = new Foo();
myBar.happy();
}
}
但是,就此而言,对象继承也是如此;继承类Bar的类Foo的对象具有can-do关系以及is-a关系,与接口的方式相同。只是它的实现是为它预先构建的。
真正的问题是,is-a- 什么,可以做什么 ?继承的类对象是-a- [父实例] 和can-do- [parent的行为] ,而接口的实现是-an- [接口实现] 因此可以 - [接口行为] 。
在程序化的大多数情况下 - 使用上面列出的关系,只评估接口,因此继承类和实现的接口共享相同的is-a和can-do质量。
HTH, 乔恩
答案 3 :(得分:2)
.NET框架的设计者使用接口来指定“具有”(或“可以做”)关系,而“是a”则使用继承来实现。
可以在.NET Framework开发人员指南的Choosing Between Classes and Interfaces部分找到相关的基本原理:
接口定义实施者必须提供的一组成员的签名。接口无法为成员提供实现细节。
因此,由于您的“程序员”和“工程师”示例类很可能具有自己的特定功能,因此使用继承更适合实现它们。
答案 4 :(得分:2)
实际上,这就是为什么大多数界面都是功能而不是名称,所以你有
IComparable,ITestable,IEnumerable
和
人类,动物,狗等
无论如何,正如已经提到的那样你必须务实,我在编码时有一个通用规则:永远不要让概念,惯例或标准实践在完成工作的过程中,最好是务实而不是学术。
所以,如果您确定界面真的更适合您的设计,那就去吧,不要担心这个问题。