也许你看到这个题目的时候,就觉得这篇博文不用看了,难道这就是题目劝退了观众。我看到过一些程序,是由面向过程的传统程序修改过来了,只是将原来的函数变成了类的成员函数,其他几乎没有什么变化,可以说是换汤不换药。可以说,会用类进行程序编写说明你掌握了C++的语言基础,但使用面向对象的方法进行程序设计才是你真正掌握了学习C++的精髓。面向对象的程序设计主要思想还是类间的关系,以及整体设计是面向对象。
由面向过程的程序过渡到面向对象的程序设计,即使你已经使用类多年,也不能说明你在程序设计过程中完全使用的是面向对象的思维,程序设计类之间的关系决定了你掌握面向对象的程度,在面向对象程序设计过程中程序员碰到的一些陷阱等我们也会涉及。
面向对象与面向过程的程序设计方法的最大的不同点就是真实程序中不同解决问题的方式。而不是陷于对于类与对象等面向对象程序设计的名词的理解。
1、我是否在用面向过程的思维在思考
面向过程的编程语言,比如C,将代码分为各个小的部分,理想情况下,每个部分完成一个单独的任务。在C语言中,如果没有过程,所有的代码都会堆到main()函数中。这样的代码难于阅读,阅读你代码的同事至少不会太开心。
其实电脑根本不关心你的代码是都在main()函数中还是分成了各个部分,各个部分都具有描述性的名字与注释。过程就是帮助你、程序员和那些阅读与维护你的代码的人等存在的抽象。其概念就是围绕你的程序的基本问题进行的构建--程序是干什么用的?用人话来说的话就是,用面向过程的思维思考。例如,通过回答如下问题开始设计一个股票选择的程序:首先,程序从互联网上获得股票报价,然后,以特定的方式对数据进行排序;接着,对排序数据进行分析,最后,输出买卖的推荐方案。当开始编码的时候,你可能就将以上的思维模型转化成C函数:retrieveQuotes(), sortQuotes(), analyzeQuotes(),和outputRecommendations()。
即使C中的过程就是指的函数,但C不是函数语言,函数这个术语与过程不同,指的是像Lisp这样的语言,该语言保用了完全不同的抽象。
过程的方法倾向于让你的程序按照一定的步骤运行。然而,一般来说,现代应用很少是线性的事件顺序。通常情况下用户可以在任何时间执行任何命令。面向过程的思维也不涉及到数据表示。像上面的那个例子,实际上就没有对于股票报价的讨论。
如果思维的过程模式听起来像是你与程序打交道的方式,也不要担心。一旦你意识到OOP只是另外一种更灵活的软件思维方式,就会很自然地接受了。
2、面向对象学说
不像面向过程的方法是基于"程序是做什么的"的问题,面向对象的方法是要问另外一个问题:"我要把真实世界当中的对象怎样进行建模?"。OOP是基于你将程序分成物理对象而不是分成任务的符号表达。一开始看起来比较抽象,当你用类、部件、属性与行为等术语来考虑物理对象时就会变得越来越清晰。
2.1、类
类是区分对象的定义。我们看一下桔子,我们如果只是通常讨论它长在树上,是一种美味的水果,与讨论一个特定的桔子,如正在把汁液滴在了我的键盘上的那个桔子,完全不是一回事儿。
如果你来回答"什么是桔子?",你是在讨论Orange这个类。所有的桔子都是水果。都长在树上。都是橙色的,都有特定的味道。类就是定义了一类物体的包装。
当描述某个特定的桔子时,就是讨论一个对象。所有的对象属于一个特定的类。因为在我书桌上的对象是一个桔子,我知道它属于桔子这个类。这样的话,我就知道它是长在树上的水果。我还可以说它是一种中等色调的橙色,味道好极了。对象是类的一个实例,一个带有区别于同类的其他实例的具有特性的元素。
举一个更具体的例子,考虑一下上面的股票选择的应用。在面向对象的程序设计中,"股票报价"就是一个类,因为它定义了构成报价的抽象符号。一个特定的报价,像"当前的科顺股份的报价"就是一个对象,因为它是类的一个特定实例。
从C转过来的同学,会把类与对象类比成类型与变量。实际上,类的语法与C结构的语法类似。
2.2、部件
如果考虑复杂真实世界中的对象,比如一架飞机,非常容易地能够看出,它是由许多小的部件构成。有机身、控制台、起落装置、发动机以及数不清的其他部件。在面向对象的程序设计中把对象分成小的部件很重要,就像在面向过程的程序设计中把复杂的任务分成小的过程一样。
部件与类一样重要,只是更小更具体。好的面向对象的程序可能会有一个Airplane类,但是如果用它来描述一架飞机的话就太大的,Airplane类处理许多更小的更易管理的部件。每个部件可以有子部件。例如,起落装置是飞机的部件,轮子是起落装置的部件。
2.3、属性
属性用于区别对象本身与其它对象。回到上面的Orange类,回想一下所有的桔子都是橙色的,有特殊的味道。这两个特点就是属性。所有的桔子都有相同的属性,只是带有不同的值。我的桔子的味道好极了,而你的桔子可能坏了,味道难闻。
你也可以在类的层次上思考属性。前面提到过,所有的桔子都是水果,长在树上。水果类有属性,具体的味道由具体的水果对象决定。类的属性由类的所有对象分享,而类的所有对象都有属性,只是其值不同。
在股票选择的例子中,一个股票报价类有几个对象属性,包括公司名字,行情显示代号,当前价格,以及其他统计信息。
属性用于描述对象的特点。回答了"什么使得这个对象不同于其他对象?"的问题。
2.4、行为
行为回答了如下两个问题:"这个对象做什么?"和"我能对这个对象做什么?",对于桔子的那个例子,它干不了什么,但我们可以对它干一些事儿。一个行为就是它能吃。像属性,你可以认为是在类层次上或者对象层次上的行为。所有的桔子可以以相同的方式被吃掉。然而,其他的行为可能是不同的,比如从斜坡上往下滚,非常圆的桔子与扁圆的行为就会不同。
股票选择的例子提供一些更实际的行为。如果你能记得的话,用面向过程的思维来考虑,就会将分析股票报价变成不同的函数。用面向对象的方式,就会决定股票报价对象分析自身。分析就变成了股票报价对象的一种行为。
在面向对象的编程中,大量的函数代码被移出过程,移进类中。通过构建具有特定行为并且定义了交互性的类。OOP提供一更丰富的技巧将代码作用于数据上。类的行为通过类成员函数来实现。
我们再次声明,在Java中,只能通过类的方式来进行程序设计。但在C++中,既可以用类的方式来进行面向对象的程序设计,也可以用面向过程的方式来进行程序设计,将其分解成用函数来表达的过程就可以了。实际上,C++标准库都是单独的函数,比如,标准库的所有算法。
2.5、综合实例
利用这些概念,可以将股票选择程序用面向对象的方式进行重新设计。
如前讨论,"股票报价"可以是一个开始的比较好的类。为了得到报价的列表,程序需要一组股票报价的符号,通常叫做Collection。好的设计应该有一个能够表达"股票报价的Collection",由更小的能够表达单个"股票报价"的部件构成。
现在来说属性,collection类至少要有一个属性,接收到的报价列表。也可以有更多的属性,比如最近访问的日期与时间。至于行为,"股票报价的Collection"能够与服务器通信获得报价,提供报价的排好序的列表。这些就是"retrieve quotes" 与"sort quotes"行为。
股票报价类具有了前面讨论的属性--名字、符号、当前报价等等。还有,它还有一个分析的行为。也可以考虑其他行为,比如买进股票、卖出股票。
生成显示部件之间的关系的图很有用。下图使用了UML类图来展示一个StockQuoteCollection包含了0个或多个(0..*) StockQuote对象,一个StockQuote对象属于一个单独的StockQuoteCollection。
我们再看一下前面提到的桔子的例子,桔子有像颜色与气味的属性,也有被吃或滚的行为。你还可以给它加上像扔、削皮、挤压等行为。还有一个桔子的属性:桔子种子集合。下图给出了Orange与Seed类的UML类图,包括相互之间的关系,一个Orange包含0个或多个(0..*) Seed,一个Seed属于一个单独的Orange。