http://www.kuqin.com/shuoit/20150809/347546.html
当C++爱上面向对象
很多第一次进入C++世界的人都会问:C++中的那两个加号到底是什么意思啊?
C++是由C语言发展而来的,它比C语言多出的两个加号,实际上是C语言的自增操作符,表示C++语言是在C语言的基础上添加了新的内容而发展形成的。如果其中一个加号代表C++在C语言的基础上增加了模板、异常处理等现代程序设计语言的新特性的话,那么另外一个加号则代表C++在C语言的基础上增加了对面向对象程序设计思想的支持。正是这两个加号所代表的新增内容,让C++在C语言的根基之上,完成了从传统到现代,从面向过程到面向对象的进化,让C++与C语言有了本质的区别。
而在C++所新增的这两个内容中,最核心的是对面向对象程序设计思想的支持,正是它的加入,才完成了从C语言到C++的华丽蜕变。那么,到底什么是面向对象?C++为什么要添加对它的支持?C++又是如何对面向对象进行支持的?别着急,且听我一一道来。
6.1 从结构化设计到面向对象程序设计
面向对象程序设计的雏形早在1960年的Simula语言中就出现过。当时程序设计领域正面临一种危机:面对越来越复杂的软硬件系统,传统的以C语言为代表的面向过程程序设计思想已越来越无法满足现实的需要——面向过程的设计无法很好地描述整个系统,同时设计结果也让人难以理解,因而给软件的实现以及后期的维护带来了巨大的挑战,项目越大越难以实现,越到项目后期越难以实现,人们正陷入一场前所未有的软件危机中。为了化解这场软件危机,人们开始寻找能够消灭“软件危机”这头怪兽的“银弹(silver bullet)”。面向对象程序设计思想正是在这种背景下应运而生,它通过强调设计与实现的可扩展性和可重复性,成功地化解了这场“软件危机”。至此以后,面向对象的程序设计思想开始在业界大行其道,逐渐成为主流。而从C语言向C++的进化刚好就发生在这个时期,自然而然地,C++也就选择了支持面向对象程序设计思想。
《人月神话》与银弹
《人月神话》是软件领域里一本具有深远影响的著作。它诞生于软件危机的背景之下,而正是这本书提出了“银弹”的概念。
在西方的神话传说中,只有被银弹击中心脏,才可以杀死怪兽。而在这本书中,作者把那些规模越来越大的、管理与维护越来越困难的软件开发项目比作传说中无法控制的怪兽,并希望有一项技术能够像银弹杀死怪兽那样,彻底地解决这场软件危机。
其实,面向对象程序设计思想并不是完全意义上的银弹,它不可能解决所有大型软件项目所遇到的问题,但是它提出了一种描述软件的更加自然的方式,在一定程度上解决了这场软件危机。
6.1.1 “自顶向下,逐步求精”的面向过程程序设计
要想了解新的面对对象思想有什么优点,最简单直接的方式就是先看看旧的面向过程思想有什么缺点。回顾前面章节中曾经学过的例子,我们在解决问题时总是按照这样的流程:先提出问题;然后分析问题的处理流程;接着根据流程需要把一个大问题划分为几个小问题;如果细分后的小问题仍然比较复杂,则进一步细分,直到小问题可以简单解决为止;实现每个子模块,解决每个小问题;最后通过主函数按照业务流程次序调用这些子模块,最终解决整个大问题,如图6-1所示。像这样从问题出发,自顶向下、逐步求精的开发思想我们称为“面向过程程序设计思想”,它描述的主要是解决问题的“过程”。
图6-1 面向过程程序设计的流程
面向过程程序设计思想诞生于20世纪60年代,鼎盛于20世纪80年代,是当时最为流行的程序设计思想。它的流行有其内在原因,跟当时其他程序设计思想相比,它有着明显的优势。
1. 程序仅由三种基本结构组成
正如第4章中所介绍的程序流程控制结构一样,面向过程程序设计思想限定程序只有顺序、选择和循环这三种基本控制结构。任何程序逻辑,无论是简单的还是复杂的,都可以用这三种基本的控制结构经过不同的组合或嵌套来实现。这就使得程序的结构相对比较简单,易于实现和维护。
2. 分而治之,各个击破
人们在解决复杂问题时,总是采用“分而治之”的策略,把大问题分解为多个小问题后,再“各个击破”并最终让大问题得到解决。面向过程程序设计思想也采取这种“分而治之”的策略,把较大的程序按照业务逻辑划分为多个子模块,然后分工逐个完成这些子模块,最后再按照业务流程把它们组织起来,最终使得整个问题得到解决。按照一定的原则,把大问题细分为小问题“各个击破”,符合人们思考问题的一般规律,其设计结果更易于理解,同时这种方法也更易于人们掌握。通过分解问题,降低了问题的复杂度,使得程序易于实现和维护。另外,部分分解后的小问题(子模块)可以重复使用,从而避免了重复开发。而多个子模块也可由多人分工协作完成,提高了开发效率。
3. 自顶向下,逐步求精
面向过程程序设计思想倡导的方法是“自顶向下,逐步求精”。所谓“自顶向下,逐步求精”,就是先从宏观角度考虑,按照功能或者业务逻辑划分程序的子模块,定义程序的整体结构,然后再对各个子模块逐步细化,最终分解到程序语句为止。这种方法使得程序员能够全面考虑问题,使程序的逻辑关系清晰明了。它让整个开发过程从原来的考虑“怎么做”变成考虑“先做什么,再做什么”,流程也更加清晰。
随着时代的发展,软件开发项目也越来越复杂。虽然面向过程程序设计思想有诸多优点,但在利用它解决复杂问题的时候,其缺点也逐渐暴露出来:在面向过程程序设计中,数据和操作是相互分离的,这就导致如果数据的结构发生变化,相应的操作函数就不得不重新改写;如果遇到需求变化或者新的需求,还可能涉及模块的重新划分,这就要修改大量原先写好的功能模块。面向过程程序设计中数据和操作相互分离的特点,使得一些模块跟具体的应用环境结合紧密,旧有的程序模块很难在新的程序中得到复用。这些面向过程程序设计思想的固有缺点使得它越来越无法适应大型的软件项目的开发,这真是“成也面向过程,败也面向过程”。于是,人们开始寻找一种新的程序设计思想。正是在这种情况下,一些新的程序设计思想开始不断涌现并逐渐取代面向过程程序设计思想,而面向对象程序设计思想就是其中的“带头大哥”。
6.1.2 万般皆对象:面向对象程序设计
面向对象程序设计(Object Oriented Programming, OOP)是对面向过程程序设计的继承和发展,它不仅汲取了后者的精华,而且以一种更接近人类思维的方式来分析和解决问题:程序是对现实世界的抽象和描述,现实世界的基本单元是物体,与之对应的,程序中的基本单元就是对象。
面向对象思想认为:现实世界是由很多彼此相关并互通信息的实体——对象组成的。大到一个星球、一个国家,小到一个人、一个分子,无论是有生命的,还是没有生命的,都可以看成是一个对象。通过分析这些对象,发现每个对象都由两部分组成:描述对象状态或属性的数据(变量)和描述对象行为或功能的方法(函数)。与面向过程将数据和对数据进行操作的函数相分开不同的是,面向对象将数据和操作数据的函数紧密结合,共同构成对象来更加准确地描述现实世界。这可以说是面向过程与面向对象两者最本质的区别。
跟现实世界相对应的,在面向对象中,我们用某个对象来代表现实世界中的某个实体,每个对象都有自己的属性和行为,而整个程序则由一系列相互作用的对象构成,对象之间通过互相操作来完成复杂的业务逻辑。比如在一个班中,有一位陈老师和50名学生,那么我们就可以用一个老师对象和50个学生对象来抽象和描述这一个班级。对这51个对象而言,有些属性是它们所共有的,比如姓名、年龄等,每个对象都有;而有部分属性则是某类对象特有的,比如老师对象有职务这个属性,而学生对象则没有。另外,老师和学生这两种对象还有各自不同的行为;比如老师对象有备课、上课、批改作业的行为;而学生对象则有听课、完成作业等行为。老师对象和学生对象各自负责自己的行为和职责,同时又相互发生联系,比如老师上课的动作需要以学生作为动作对象。通过对象之间的相互作用,整个班级就可以正常运作。整个面向对象分析设计的结果,跟我们的现实世界非常接近,自然也就更容易理解和实现了。老师对象如图6-2所示。
图6-2 用面向对象思想将老师抽象成对象
知道更多:面向对象编程的重要性在哪?
这一点,也许可以从面向对象的诞生说起。
在面向对象出生之前,有一个叫做面向过程的人,它将整个待解决的问题,抽象为描述事物的数据以及描述对数据进行处理的函数,或者说数据处理过程。当问题规模比较小,需求变化不大的时候,面向过程工作得很好。
可是(任何事物都怕“可是”二字),当问题的规模越来越大越来越复杂,需求变化越来越快的时候,面向过程就显得力不从心了。想象一下,当我们根据需求变化修改了某个结构体,就不得不修改与之相关的所有过程函数,而一个过程函数的修改,往往又会涉及到其他数据结构,在系统规模较小的时候,这还比较容易解决,可是当系统规模越来越大,特别是涉及到多人协作开发的时候,这简直就是一场噩梦。这就是那场著名的软件危机。
为了解决这场软件危机,面向对象应运而生了(有问题的出现,必然就有解决问题的方法的出现,英雄人物大都是这样诞生的)。
我们知道,面向对象的三板斧分别是封装、继承和多态:它用封装将问题中的数据和对数据进行处理的函数结合在了一起,形成了一个整体的类的概念,这样更加符合人的思维习惯,更利于理解,自然在理解和抽象一些复杂系统的时候也更加容易;它用继承来应对系统的扩展,在原有系统的基础上,只要简单继承,就可以完成系统的扩展,而无需重起炉灶;它用多态来应对需求的变化,统一的借口,却可以有不同的实现。
可以说,面向对象思想用它的三板斧,在一定程度上解决了软件危机,这就是它重要性的根本体现。