在多态性的C#示例中,有一个Cat类,它继承了一个名为AnimalBase的类和一个名为IAnimal的接口。
有问题的链接是:http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming
我的问题是,为什么同时使用基类和接口?为什么不是其中之一呢?我的想法是,实现多态性只需要一个抽象类。
谢谢
发布于 2008-12-18 14:10:26
“从基类继承允许你继承行为,而实现一个接口只允许你指定交互”这句话是绝对正确的。
但更重要的是,接口允许静态类型语言继续支持多态性。面向对象的纯化论者会坚持认为,一种语言应该提供继承、封装、模块化和多态性,以便成为一种功能齐全的面向对象语言。在动态类型或鸭子类型的语言(如Smalltalk )中,多态性是微不足道的;然而,在静态类型语言(如Java或C# )中,多态性远非微不足道(实际上,从表面上看,它似乎与强类型的概念不一致)。
让我来演示一下:
在动态类型(或鸭子类型)语言(如Smalltalk)中,所有变量都是对对象的引用(仅此而已)。所以,在Smalltalk中,我可以这样做:
|anAnimal|
anAnimal := Pig new.
anAnimal makeNoise.
anAnimal := Cow new.
anAnimal makeNoise.
该代码:
makeNoise
分配给猪。相同的Java代码看起来像这样(假设Duck和Cow是Animal的子类:
Animal anAnimal = new Pig();
duck.makeNoise();
anAnimal = new Cow();
cow.makeNoise();
这一切都很好,直到我们介绍类蔬菜。蔬菜和动物有一些相同的行为,但不是全部。例如,动物和蔬菜可能都能生长,但很明显蔬菜不会发出噪音,动物也不能收获。
在Smalltalk中,我们可以这样写:
|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.
aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.
这在Smalltalk中工作得很好,因为它是鸭子类型的(如果它走起来像鸭子,叫起来像鸭子-它就是鸭子)。在这种情况下,当消息被发送到对象时,将在接收方的方法列表上执行查找,如果找到匹配的方法,则调用该方法。如果不是,就会抛出某种NoSuchMethodError异常--但这一切都是在运行时完成的。
但是在Java这种静态类型的语言中,我们可以为变量分配什么类型呢?玉米需要从蔬菜中继承,以支持生长,但不能从动物中继承,因为它不会制造噪音。Cow需要从Animal继承以支持makeNoise,但不能从蔬菜继承,因为它不应该实现收割。看起来我们需要多重继承--从多个类继承的能力。但事实证明,这是一个相当困难的语言特性,因为出现了所有的边缘情况(当多个并行超类实现相同的方法时会发生什么?)
随之而来的是接口...
如果我们创建Animal和declare类,每个类都实现Growable,我们就可以声明我们的Cow是Animal,我们的玉米是蔬菜。我们也可以声明动物和蔬菜都是可生长的。这让我们可以写这段代码来增长一切:
List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());
for(Growable g : list) {
g.grow();
}
它让我们这样做,制造动物的声音:
List<Animal> list = new ArrayList<Animal>();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
a.makeNoise();
}
鸭型语言的最大优点是您获得了非常好的多态性:类提供行为所需做的全部工作就是提供方法(还有其他权衡,但这是讨论类型时最重要的权衡)。只要每个人都表现得很好,并且只发送与定义的方法匹配的消息,一切都是好的。缺点是下面的那种错误直到运行时才会被捕获:
|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.
静态类型的语言提供了更好的“契约式编程”,因为它们将在编译时捕获以下两种错误:
Animal farmObject = new Corn(); // Compiler error: Corn cannot be cast to Animal.
farmObject makeNoise();
--
Animal farmObject = new Cow();
farmObject.harvest(); // Compiler error: Animal doesn't have the harvest message.
So....to总结:
发布于 2008-12-18 13:15:19
当您想要重用行为时,可以使用基类
当您想要控制类如何与其他对象交互时,可以使用接口。它以精确的方式定义了交互。
根据我的经验,当你想要重用行为的时候,你想要控制类如何交互的次数就相形见绌了。
发布于 2008-12-18 13:15:51
拥有一个抽象类可以让你以一种通用的方式实现一些/大多数成员。拥有接口意味着当您想要将其用于多态性时,您不再局限于仅使用该抽象基类。
我看不出两者兼得有什么矛盾。
https://stackoverflow.com/questions/379282
复制