9

今天有人告诉我,C#中的接口实现只是“Can-Do”关系,而不是“Is-A”关系。这与我长期以来对 LSP(Liskov Substitution Principle)的信念相冲突。我一直认为所有继承都应该意味着“Is-A”关系。

所以,如果接口实现只是一个“可以做”的关系。如果有一个接口“IHuman”和“IEngineer”,一个类“Programmer”继承自“IHuman”和“IEngineer”怎么办?当然,“程序员”是“IHuman”和“IEngineer”。

如果只是“Can-Do”关系,是否意味着我们不能期望“Programmer”实例的行为在被视为 IHuman 和被视为 IEngineer 时可能会有所不同?

4

5 回答 5

17

根据我的经验,考虑“是一个”和“可以做”的关系并没有太大帮助。你很快就会遇到问题。基本上,这是现实世界和 OO 之间的阻抗不匹配。不管有多少人真正谈论对现实世界进行建模,您都需要从根本上了解类型之间的关系在您使用的平台上意味着什么。

有时接口可以用作能力,有时它们可​​以代表更多的正常“is-a”关系。我不会对此过于挂念——只要确保你了解他们能做什么和不能做什么。

于 2008-11-01T08:35:36.830 回答
6

我倾向于将接口视为行为契约。IComparable 和 IEnumerable 等接口是经典示例。

在您给出的示例中,IHuman 和 IEngineer 并不是真正的行为。

于 2008-11-01T08:28:26.593 回答
4

发现这个问题很晚,但我想插话。

C# 中的接口具有 is-a 关系,但没有 is-an-object。相反,is-an-implementation。

换句话说,对于实现 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 本身没有实现,所以可以做的关系也适用。

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 关系,与接口相同。只是它的实现是为它预先构建的。

真正的问题是,是什么,能做什么?继承的类对象是-a- [父实例]和 can-do- [父行为],而接口的实现是-an- [接口实现]因此 can-do- [接口行为]

在大多数使用程序化 is-a 关系(例如上面列出的那些)的情况下,只评估接口,因此继承的类和实现的接口共享相同的 is-a 和 can-do 质量。

HTH,乔恩

于 2009-01-07T21:11:37.470 回答
2

.NET 框架的设计者使用接口来指定“有”(或“可以”)关系,而“是”是使用继承实现的。

可以在 .NET Framework 开发人员指南的选择类和接口部分中找到这样做的理由:

接口定义了实现者必须提供的一组成员的签名。接口不能为成员提供实现细节。

因此,由于您的“程序员”和“工程师”示例类很可能带有自己的特定功能,因此使用继承来实现它们会更合适。

于 2008-11-01T08:37:06.733 回答
2

实际上这就是为什么大多数接口都是功能而不是名称,所以你有

IComparable, ITestable, IEnumerable

人、动物、狗等

无论如何,正如已经提到的,你必须务实,我在编码时有一个一般规则:永远不要让概念、约定或标准实践妨碍完成工作,务实而不是学术.

因此,如果您确定界面确实更适合您的设计,那就去做吧,不要担心诸如此类的问题。

于 2008-11-01T17:17:43.187 回答