面向对象的三大特性:封装、继承和多态。这是任何一本面向对象设计的书里都会介绍的,但鲜有讲清楚的,新手看了之后除了记住几个概念外,并没真正了解他们的意义。前几天在youtube上看了Bob大叔讲解的SOLID原则,其中有一段提到面向对象的三大特性,收获很多,但是我并不完全赞同他的观点,这里谈谈我的想法:
封装
『封装』第一层含义是信息隐藏。这是教科书里都会讲解的,把类或模块的实现细节隐藏起来,对外只提供最小的接口,也就是所谓的『最小知识原则』。有个共识,正常的程序员能理解的代码在一万行左右。这是指在理解代码的实现细节的情况下,正常的程序员能理解的代码的规模。比如一个文件系统,FAT、NTFS、EXT4和YAFFS2等,它们的实现是比较复杂的,少则几千行代码,多则几万行,要理解它们的内部实现是很困难的,但是如果屏蔽它们的内部实现细节,只是要了解它们对外的接口,那就非常容易了。
关于『封装』的这一层含义,Bob大叔提出了惊人的见解:『封装』不是面向对象的特性,面向过程的C语言比面向对象的C++/Java在『封装』方面做得更好!证据也是很充分:C语言把函数的分为内部函数和外部函数两类。内部函数用static修饰,放在C文件中,外部函数放在头文件中。你完全不知道内部函数的存在,即使知道也没法调用。而像在C++/Java中,通过public/protected/private/friend等关键字,把函数或属性分成不同的等级,这把内部的细节暴露给使用者了,使用者甚至可以绕过编译器的限制去调用私有函数。所以在信息隐藏方面,『封装』不但不是面向对象的特性,而且面向对象减弱了『封装』。
『封装』的第二层含义是把数据和行为封装在一起。我觉得这才是面向对象中的『封装』的意义所在,而一般的教科书里并没提及或强调。面向过程的编程中,数据和行为是分离的,面向对象的编程则是把它们看成一个有机的整体。所以,从这一层含义来看,『封装』确实是面向对象的『特性』。
面向对象是一种思维方式,而不是表现形式。在C语言中,可以实现面向对象的编程,事实上,几乎所有C语言开发的大型项目,都是采用了面向对象的思想开发的。把C语言说成面向过程的语言是不公平的,是不是面向对象的编程主要是看指导思想,而不是编程语言。你用C++/Java可以写面向过程的代码,也可以用C语言写面向对象的代码。
继承
类就是分类的标准,也就是一类事物,一类具有相同属性和行为对象的抽象。比如动物就是一个类,它描述了所有具有动物这个属性的事物的集合。狗也是一个类,它具有动物所有的特性,我们说狗这个类继承了动物这个类,动物是狗的父类,狗是动物的子类。在C语言中也可以模拟继承的效果,比如:
struct Animal { ... };
struct Dog { struct Animal animal; ... }
struct Cat { struct Animal animal; ... }
因为C语言也可以实现『继承』,所以Bob大叔认为『继承』也不算不上是面向对象的『特性』。但是我觉得,C语言中实现『继承』的方式,需要用面向对象的思维来思考才能理解,否则纯粹从数据结构的方式来看上面的例子,理解起来就会大相径庭:animal是Dog的一个成员,所以Animal可以看成是Dog的一部分!Is a 变成了has a。只有在面向对象的思想中,说『继承』才有意义,所以说『继承』是面向对象的『特性』并不牵强。
在C语言里实现多重继承更是非常麻烦了,记得glib里实现了接口的多重继承,但是用起来还是挺别扭的,对新手来说更是难以理解。多重继承在某些情况下,会对编译器造成歧义,比菱形继承结构:A是基类,B和C是它的两个子类,D从B和C中继承过来,如果B和C都重载了A的一个函数,编译器此时就没法区分用B的还是C的了(当然这是可以解决的)。
像Bob大叔说的,Java没有实现多重继承,并不是多重继承没有用。而是为了简化编译器的实现,C#没有实现多重继承,则是因为Java没有实现多重继承:)
除了接口多重继承是必不可少的,类的多重继承在现实中也是很常见的。比如:狼和狗都是狗科动物的子类,猫和老虎都是猫科动物的子类。狗科动物和猫科动物都是动物的子类。但是猫和狗都是家畜,老虎和狼都是野生动物。猫不但要继承猫科动物的特性,还继承家畜的特性。类就是分类的标准,而混用不同的分类标准是多重继承的主要来源。多重继承可以用其他方式实现,比如traits和mixin。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/482580.html