这几天飘着看了些关于多态的文章,晕得难受,写写。
(0)广义上的多态
从广义上说,多态性是指一段程序能够处理多种类型对象的能力。
在C++语言中,这种多态性可以通过强制多态、重载多态、类型参数化多态、包含多态4种形式来实现。
其中,强制多态大概指的是强制类型转换;重载多态是overload;类型参数化多态大概指的是类模板和函数模板;包含多态则是指通过子类型化,1个程序段既能处理类型t的对象,也能够处理类型t的子类型s的对象。
本文主要讨论包含多态(注意:如果不特意说明,那么下文提到的多态都是在说包含多态),也略微涉及重载多态。
(1)什么是多态?
多态就是一个对象的多种状态。
多态是指,不同对象对应于同一动作语义具有不同的实际行为。
多态就是使得你能够用一种统一的方式来处理一组各具个性却同属一族的不同个体的机制。
多态意味着许多类可以提供同样的属性或者方法,而且调用者在调用这些属性或方法之前,不必知道某个对象属于什么类。
一日300多人一起打扫卫生,不懂多态的领导发号施令道:a去扫地、b去擦窗户、c去打水......话未毕,累倒当场,立仆;上级随即派来一懂得多态的领导,不愧青年才俊,他只发布了一道命令:大家都去干活。于是各司其职,不一会事毕.....
比如下面这个例子:
Dim Dog As clsIDog
For Each Dog In Kennel
Dog.Eat
Next Dog
假设kennel里有很多种狗,有藏獒也有京巴,它们吃东西的具体形式不大一样。但是用他们的父类dog对象,就可以在程序运行期间动态地调用子类的方法。总之,这里说的多态,就是父类调用子类的方法,就是后期绑定。
(2)多态是如何实现的?
多态是通过overide实现的,也就是子类重写或覆盖(这两个词都是override的翻译)基类的方法,从而使同样的接口可以有不同的实现。
所以,我们也可以说“继承”是多态的基础,因为首先子类要先“继承”父类。不过的话,这个继承和通常意义上的继承又不一样,在语法上真正的继承通常用extends关键字,而多态的继承用implements关键字。
(3)多态和继承的关系
多态和继承很像,但它们不是一回事,事实上它们在很多意义上是互补的。
比如说,在我看来,提高开发效率是面向对象最大的好处。继承和多态作为OO的三大特性之二(另外一个是封装),帮助提高开发效率的途径是不一样的。继承的好处在于有助于代码的重用;多态的好处在于提高代码的适用性。
继承的话,其实是允许子类用父类的方法,也就是使得父类的代码得以重用,减少了写重复代码的工作。多态的话,其实是允许父类用子类的方法,也就是当你扩充了子类,使用父类方法的代码不必变化,也就是提高了代码的适用性,减少了写新代码的工作。
再比如说,各语言对继承和多态的支持是不一样的。在JAVA和.Net里,继承是用extends关键字,多态是用implements关键字;在VB6里不支持继承,只支持implements。
再再比如说,普通意义上的类继承实际上是实现的继承,而多态则是接口的继承。现在通常认为实现继承是一种强耦合,应该被包含关系取代(见[1]41楼)。
MSDN的VB6文档里提到COM不支持继承,原因是:版本更新的时候,你需要重新编译软件。如果变动发生在继承树的高层,这可能会巨麻烦。而接口继承则被VB6支持。
COM设计的原则就是接口和实现解耦,和编译器解耦,和语言解耦。所以COM组件的重用性高。
再再再比如说,多态和继承是一组相反的调用过程,多态是父类调子类的方法,继承是子类调父类的过程。其技术上的区别是绑定时期,晚期绑定一定是多态。
还有,有人说,多态+继承 使面向对象的代码复用思想更全面,继承负责共性的部分,多态负责不同的部分,各自为重用做了贡献。
(4)多态和接口的关系
接口提供了在 Visual Basic 中实现多态性的另一种方法。接口描述属性和方法的方式与类相似,但与类不同,接口不能提供任何实现。多个接口具有允许软件组件的系统不断发展而不破坏现有代码的优点。
若要使用接口实现多态性,应在几个类中以不同的方式实现接口。客户端应用程序可以以完全相同的方式使用旧实现或新实现。基于接口的多态性的优点是,不需要重新编译现有的客户端应用程序就可以使用新的接口实现。
这个其实是COM的重要好处之一。但是任何事物都有两面,另一面是,由于接口的不可变更,导致组件升级后,留下好多垃圾接口的实现。
(5)多态和重载(overload)/覆盖(override)的关系
overload是重载,指的是相同的函数名,可以有不同的函数签名(就是参数列表可以不同)。这个是早期绑定,而且不是面向对象的特征。
override一般翻译成覆盖或者重写。指的是子类继承父类的时候,子类重新实现父类的同名方法(参数列表也要完全相同)。
overload和多态没啥关系;override是多态实现的基础。
重载是静态的,是编译期间决定的。而覆盖是动态的,是后期绑定的。
(6) 参考链接
[1] http://topic.csdn.net/u/20090630/22/c57e315f-da22-4d2f-ab79-13d519416d16.html
[2] http://topic.csdn.net/u/20090630/22/0f51f742-7cc8-4786-8a5a-a831626d1fcb.html
[3] http://topic.csdn.net/u/20090630/22/c80bdb35-6eaf-4791-bdcf-27a00057d739.html
[4] http://www.cnblogs.com/Samuel/archive/2004/12/12/polymorphism.html
[5] http://www.cnblogs.com/idior/archive/2005/03/06/113727.aspx
[6] http://www.cnblogs.com/allenlooplee/archive/2004/11/02/59519.html
[7] http://topic.csdn.net/t/20061107/11/5138978.html
[8] http://www.tzwhx.com/newOperate/html/1/11/114/11520.html
[9] http://www.jb51.cc/article/p-yrwkloho-qw.html
[10] http://www.jb51.cc/article/p-cjitfuvg-qw.html
注1:[1]-[3]是在CSDN论坛上与多位网友的讨论帖,感谢ahao、steedhorse等网友。里面有些内容很好,但并没有完全摘录,因为有些与本文主题无关。
注2:我的开发经验有限,本文只是基于不深入的思考和道听途说的讨论及网文得到的一些结论,可能会有错误。
注3:多态在VB6里的具体实现例子可以看这个博文。MSDN里也有例子,查polymorphism就可以。