面向对象三大特性的意义讲解

  

面向对象的三大特性:封装,继承和多态,这是任何一本面向对象设计的书里都会介绍的,但鲜有讲清楚的,新手看了之后除了记住几个概念外,并没真正了解他们的意义。前几天在youtube上看了鲍勃大叔讲解的固体原则,其中有一段提到面向对象的三大特性,收获很多,但是我并不完全赞同他的观点,这里谈谈我的想法:

  

<强>封装
  

  “第

"封装一层含义是信息隐藏。这是教科书里都会讲解的,把类或模块的实现细节隐藏起来,对外只提供最小的接口,也就是所谓的“最小知识原则”。有个共识,正常的程序员能理解的代码在一万行左右。这是指在理解代码的实现细节的情况下,正常的程序员能理解的代码的规模。比如一个文件系统,脂肪,NTFS, EXT4和YAFFS2等,它们的实现是比较复杂的,少则几千行代码,多则几万行,要理解它们的内部实现是很困难的,但是如果屏蔽它们的内部实现细节,只是要了解它们对外的接口,那就非常容易了。

  

关于“封装”的这一层含义,鲍勃大叔提出了惊人的见解:“封装”不是面向对象的特性,面向过程的C语言比面向对象的c++或Java在“封装”方面做得更好!证据也是很充分:C语言把函数的分为内部函数和外部函数两类。内部函数用静态修饰,放在C文件中,外部函数放在头文件中。你完全不知道内部函数的存在,即使知道也没法调用。而像在c++/Java中,通过公共/保护/私人/朋友等关键字,把函数或属性分成不同的等级,这把内部的细节暴露给使用者了,使用者甚至可以绕过编译器的限制去调用私有函数,所以在信息隐藏方面,“封装”不但不是面向对象的特性,而且面向对象减弱了“封装”。

  

"封装”的第二层含义是把数据和行为封装在一起。我觉得这才是面向对象中的“封装”的意义所在,而一般的教科书里并没提及或强调。面向过程的编程中,数据和行为是分离的,面向对象的编程则是把它们看成一个有机的整体,所以,从这一层含义来看,“封装”确实是面向对象的“特性”。

  

面向对象是一种思维方式,而不是表现形式。在C语言中,可以实现面向对象的编程,事实上,几乎所有C语言开发的大型项目,都是采用了面向对象的思想开发的,把C语言说成面向过程的语言是不公平的,是不是面向对象的编程主要是看指导思想,而不是编程语言。你用c++/Java可以写面向过程的代码,也可以用C语言写面向对象的代码。

  

<强>继承
  

  

类就是分类的标准,也就是一类事物,一类具有相同属性和行为对象的抽象。比如动物就是一个类,它描述了所有具有动物这个属性的事物的集合。狗也是一个类,它具有动物所有的特性,我们说狗这个类继承了动物这个类,动物是狗的父类,狗是动物的子类。在C语言中也可以模拟继承的效果,比如:

        结构体动物{   …   };            struct狗{   结构体动物动物;   …   }            struct猫{   结构体动物动物;   …   }      

因为C语言也可以实现“继承”,所以鲍勃大叔认为“继承”也不算不上是面向对象的“特性”。但是我觉得,C语言中实现“继承”的方式,需要用面向对象的思维来思考才能理解,否则纯粹从数据结构的方式来看上面的例子,理解起来就会大相径庭:动物是狗的一个成员,所以动物可以看成是狗的一部分!是一个变成了拥有。只有在面向对象的思想中,说“继承”才有意义,所以说“继承”是面向对象的“特性”并不牵强。

  

在C语言里实现多重继承更是非常麻烦了,记得glib里实现了接口的多重继承,但是用起来还是挺别扭的,对新手来说更是难以理解。多重继承在某些情况下,会对编译器造成歧义,比菱形继承结构:一个是基类,B和C是它的两个子类,D从B和C中继承过来,如果B和C都重载了一个的一个函数,编译器此时就没法区分用B的还是C的了(当然这是可以解决的)。

  

像鲍勃大叔说的,Java没有实现多重继承,并不是多重继承没有用。而是为了简化编译器的实现,c#没有实现多重继承,则是因为Java没有实现多重继承:)

  

除了接口多重继承是必不可少的,类的多重继承在现实中也是很常见的。比如:狼和狗都是狗科动物的子类,猫和老虎都是猫科动物的子类。狗科动物和猫科动物都是动物的子类。但是猫和狗都是家畜,老虎和狼都是野生动物。猫不但要继承猫科动物的特性,还继承家畜的特性。类就是分类的标准,而混用不同的分类标准是多重继承的主要来源。多重继承可以用其他方式实现,比如特征和mixin。

面向对象三大特性的意义讲解