当前位置:  开发笔记 > 编程语言 > 正文

为什么在多态中使用基类和接口?

如何解决《为什么在多态中使用基类和接口?》经验,为你挑选了2个好方法。

在多态性的C#示例中,有一个Cat类,它继承了一个名为AnimalBase的类和一个名为IAnimal的接口.

相关链接是:http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming

我的问题是,为什么要使用基类和接口?为什么不是一个或另一个?我认为只有一个抽象类才能实现多态性.

谢谢



1> RS Conley..:

如果要重用BEHAVIOR,则使用基类

当您想要控制类与其他对象的INTERACTS时,使用接口.它以精确的方式定义交互.

根据我的经验,您希望控制类交互方式的次数使您希望重用行为的次数相形见绌.



2> Jared..:

"从基类继承允许继承BEHAVIOR,而实现接口只允许您指定INTERACTION"的语句是绝对正确的.

但更重要的是,接口允许静态类型语言继续支持多态.面向对象的纯粹主义者会坚持认为语言应该提供继承,封装,模块化和多态性,以便成为一个功能齐全的面向对象语言.在动态类型 - 或鸭类型 - 语言(如Smalltalk)中,多态性是微不足道的; 然而,在静态类型语言(如Java或C#)中,多态性远非微不足道(事实上,从表面上看,它似乎与强类型的概念不一致.)

让我来证明:

在动态类型(或鸭子类型)语言(如Smalltalk)中,所有变量都是对象的引用(没有更多,仅此而已.)因此,在Smalltalk中,我可以这样做:

|anAnimal|    
anAnimal := Pig new.
anAnimal makeNoise.

anAnimal := Cow new.
anAnimal makeNoise.

那段代码:

    声明一个名为anAnimal的局部变量(请注意,我们不指定变量的TYPE - 所有变量都是对象的引用,不多也不少.)

    创建名为"Pig"的类的新实例

    将Duck的新实例分配给变量anAnimal.

    将信息发送makeNoise给猪.

    使用牛重复整个事情,但将其分配给与Pig相同的确切变量.

相同的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,但不能继承VEG,因为它不应该实现收获.看起来我们需要多重继承 - 从多个类继承的能力.但是由于弹出的所有边缘情况(当多个并行超类实现相同的方法时会发生什么?等),结果证明这是一个相当困难的语言特性.

接下来的接口......

如果我们制作动物和蔬菜类,每个实施Growable,我们可以宣布我们的牛是动物,我们的玉米是蔬菜.我们还可以宣称动物和蔬菜都是可以生长的.这让我们写这个来增长一切:

List list = new ArrayList();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());

for(Growable g : list) {
   g.grow();
}

它让我们这样做,发出动物的声音:

List list = new ArrayList();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
  a.makeNoise();
}

duck-typed语言的最大优点是你得到了非常好的多态性:所有类都必须提供行为提供方法(还有其他权衡,但在讨论打字时这是最重要的.)只要每个人都玩得很好,只发送符合定义方法的消息,一切都很好.缺点是下面的错误类型直到运行时才被捕获:

|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.

所以....总结一下:

    接口实现允许您指定对象可以执行的操作(交互),而类继承允许您指定应该如何完成(实现).

    接口为我们提供了许多"真正的"多态性的好处,而不会牺牲编译器类型检查.

推荐阅读
linjiabin43
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有