为什么在多态中同时使用基类和接口? [英] Why the use of both a base class and an interface in polymorphism?

查看:110
本文介绍了为什么在多态中同时使用基类和接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In the C# example of polymorphism, there is a Cat class which inherits a class called AnimalBase and an interface called IAnimal.

相关链接为: http://en.wikipedia.org/wiki/Polymorphism_in_object面向程序的编程

我的问题是,为什么同时使用基类和接口?为什么不选一个?我当时认为,实现多态性只需要一个抽象类即可.

My question is, why is both a base class and an interface used? Why not one or the other? I was of the school of thought that only an abstract class would be required for implementing polymorphism.

谢谢

推荐答案

从基类继承允许您继承行为,而实现接口仅允许您指定交互"的说法是完全正确的.

The statement that "inheriting from a base class allows you to inherit BEHAVIOR, whereas implementing an interface only lets you specify INTERACTION" is absolutely true.

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

But more importantly, interfaces allow statically typed languages to continue to support polymorphism. An Object Oriented purist would insist that a language should provide inheritance, encapsulation, modularity and polymorphism in order to be a fully-featured Object Oriented language. In dynamically-typed - or duck typed - languages (like Smalltalk,) polymorphism is trivial; however, in statically typed languages (like Java or C#,) polymorphism is far from trivial (in fact, on the surface it seems to be at odds with the notion of strong typing.)

让我演示一下:

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

In a dynamically-typed (or duck typed) language (like Smalltalk), all variables are references to objects (nothing less and nothing more.) So, in Smalltalk, I can do this:

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

anAnimal := Cow new.
anAnimal makeNoise.

该代码:

  1. 声明一个称为anAnimal的局部变量(请注意,我们不指定变量的TYPE-所有变量都是对一个对象的引用,更多也不少.)
  2. 创建名为"Pig"的类的新实例
  3. 将Duck的新实例分配给变量anAnimal.
  4. 将消息makeNoise发送到猪.
  5. 使用一头牛重复整个过程,但是将其分配给与Pig相同的确切变量.
  1. Declares a local variable called anAnimal (note that we DO NOT specify the TYPE of the variable - all variables are references to an object, no more and no less.)
  2. Creates a new instance of the class named "Pig"
  3. Assigns that new instance of Duck to the variable anAnimal.
  4. Sends the message makeNoise to the pig.
  5. Repeats the whole thing using a cow, but assigning it to the same exact variable as the Pig.

相同的Java代码如下所示(假设Duck和Cow是Animal的子类:

The same Java code would look something like this (making the assumption that Duck and Cow are subclasses of Animal:

Animal anAnimal = new Pig();
duck.makeNoise();

anAnimal = new Cow();
cow.makeNoise();

这很好,直到我们介绍蔬菜课.蔬菜与动物具有某些相同的行为,但不是全部.例如,动植物都可以生长,但显然蔬菜不会发出声音,动物也无法收获.

That's all well and good, until we introduce class Vegetable. Vegetables have some of the same behavior as Animal, but not all. For example, both Animal and Vegetable might be able to grow, but clearly vegetables don't make noise and animals cannot be harvested.

在Smalltalk中,我们可以这样写:

In Smalltalk, we can write this:

|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.

aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.

这在Smalltalk中非常有效,因为它是鸭子类型的(如果它走路像鸭子一样,而像鸭子一样嘎嘎叫-是鸭子.)在这种情况下,当消息发送到对象时,将进行查找在接收者的方法列表上执行,如果找到匹配的方法,则将其调用.如果没有,则会抛出某种NoSuchMethodError异常-但这都是在运行时完成的.

This works perfectly well in Smalltalk because it is duck-typed (if it walks like a duck, and quacks like a duck - it is a duck.) In this case, when a message is sent to an object, a lookup is performed on the receiver's method list, and if a matching method is found, it is called. If not, some kind of NoSuchMethodError exception is thrown - but it's all done at runtime.

但是在Java(一种静态类型的语言)中,我们可以为变量分配什么类型?玉米需要从蔬菜继承来支持生长,但不能从动物继承,因为它不会产生噪音.母牛需要从动物继承以支持makeNoise,但不能从蔬菜继承,因为它不应该实现收获.看来我们需要多重继承-从多个类继承的能力.但是由于弹出了所有的极端情况(这是一个非常困难的语言功能)(当多个并行超类实现相同方法时会发生什么?等等)

But in Java, a statically typed language, what type can we assign to our variable? Corn needs to inherit from Vegetable, to support grow, but cannot inherit from Animal, because it does not make noise. Cow needs to inherit from Animal to support makeNoise, but cannot inherit from Vegetable because it should not implement harvest. It looks like we need multiple inheritance - the ability to inherit from more than one class. But that turns out to be a pretty difficult language feature because of all the edge cases that pop up (what happens when more than one parallel superclass implement the same method?, etc.)

随处可见的接口...

Along come interfaces...

如果我们将动物和蔬菜类别设为可种植",则可以将其声明为牛是动物,而玉米是蔬菜".我们还可以声明动物和蔬菜都是可种植的.这使我们可以编写此代码来扩展所有内容:

If we make Animal and Vegetable classes, with each implementing Growable, we can declare that our Cow is Animal and our Corn is Vegetable. We can also declare that both Animal and Vegetable are Growable. That lets us write this to grow everything:

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

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

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

And it lets us do this, to make animal noises:

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

duck型语言的最大优点是您获得了非常不错的多态性:提供行为的类所要做的全部工作就是提供方法(还有其他折衷方案,但这是讨论类型时的主要问题).只要每个人都表现得很好,并且只发送与定义的方法匹配的消息,一切都很好.缺点是直到运行时才捕获以下错误:

The biggest advantage to the duck-typed language is that you get really nice polymorphism: all a class has to do to provide behavior is provide the method (there are other tradeoffs, but this is the big one when discussing typing.) As long as everyone plays nice, and only sends messages that match defined methods, all is good. The downside is that the kind of error below isn't caught until runtime:

|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.

静态类型的语言提供了更好的按合同编程",因为它们将在编译时捕获以下两种错误:

Statically-typed languages provide much better "programming by contract," because they will catch the two kinds of error below at compile-time:

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.

所以...总结一下:

  1. 接口实现允许您指定对象可以进行哪些操作(交互),而类继承则可以指定应如何进行操作(实现).

  1. Interface implementation allows you to specify what kinds of things objects can do (interaction) and Class inheritance lets you specify how things should be done (implementation).

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

Interfaces give us many of the benefits of "true" polymorphism, without sacrificing compiler type checking.

这篇关于为什么在多态中同时使用基类和接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆