#include <stdio.h>
int main() {
int c;
while((c = getchar()) != EOF) {
putchar(c);
}
return 0;
}
这个程序和Hello world是一个级别的,你从键盘输入一个字符,它就显示一个字符。但是它却蕴含了『多态』最精妙的招式。比如说输入吧,getchar是从标准输入(STDIN)读入一个字符,键盘输入是缺省的标准输入,但是键盘输入只是众多标准输入(STDIN)中的一种。你可以从任何一个IO设备读取数据:从网络、文件、内存和串口等等,换成任何一种输入,这个程序都不需要任何改变。
具体实现变了,调用者不需要修改代码,而且它根本不用重新编译,甚至不用重启应用程序。这就是接口的威力,也是『多态』的功劳。
上面的程序是如何做到的呢?IO设备的驱动是一套接口,它定义了打开、关闭、读和写等操作。对实现者来说,不管数据从哪里来,要到哪里去,只要实现接口中定义的函数即可。对使用者来说,完全不同关心它具体的实现方式。
『多态』不但是隔离变化的基础,也是代码重用的基础。公共函数的重用是有价值的,在面向过程的开发中也很容易做到这种重用。但现实中的重用没那么简单,就连一些大师也感叹重用太难。比如,你可能需要A这个类,你把它拿过来时,发现它有依赖B这个类,B这个类有依赖C这个类,搞到最后发现,它还依赖一个看似完全不相关的类,重用的念头只好打住。如果你觉得夸张了,你可以尝试从一个数据库(如sqlite)中,把它的B+树代码拿出来用一下。
在『多态』的帮助下,情况就会大不相同了。A这个类依赖于B这个类,我们可以把B定义成一个接口,让使用A这个类的使用者传入进来,也就是所谓的依赖注入。如果你想重用A这个类,你可以为它定制一个B接口的实现。比如,我最近在一个只有8K内存的硬件上,为一块norflash写了一个简单的文件系统(且看作是A类),如果我直接去调用norflash的API(且看作是B类),就会让文件系统(A类)与norflash的API(B类)紧密耦合到一起,这就会让文件系统的重用性大打折扣。










