面向对象的概念,已经提出几十年了(在上个世纪八十年代提出,可能更早),但是好像不太好理解,我觉得没有学过计算机的人理解起来反而容易些。对计算机原理性质的课程研究的时间一长觉得面向过程 的程序设计简直就是天经地义了。
面向对象里有些概念,当然,任何一个理论都需要有概念。概念定义了理论的模型和讨论问题的一些术语,仅此而已。下面稍稍说一下面向对象里的概念问题,当然,不会像《面向对象的程序设计》之类书中那样的纯理论介绍,可以结合例子来看。
先看一个最本质的概念
抽象是对复杂的概念,过程或者现实世界的对象的简化或者建模。它帮助人们在某个合适的层次上认识事物,这意味着不同的人对同一个概念会建立完全不同的抽象。对于那些整体上十分复杂,难以理解的事物,一个好的抽象应该能够突出有关的特性和行为。抽象还可以帮助我们理解一个大模型中的各个组件是如何交互的。在面向对象的世界中,这些交互的组件被称之为对象
抽象,是人类认识和掌握复杂客观世界,研究各种用于指导实践的理论知识的必然前提。抽象这个概念本身很抽象,举个例子来看:在小学一年级,学习算数的时候,课本上基本没有“2+3=5”这样的算式,取而代之的是一些苹果或者小鸭子,比如2个苹果加上3个苹果等于5个苹果之类。到了高年级,我们看到苹果的时候,已经可以在大脑中抽象出数字来了。这是从具体的物体中抽出来一种属性,这个过程我们称之为抽象!
顺便说一下:数学的神奇之处就在于,它看似跟现实世界没有任何关系,但是现实世界就是离不开它。数学是一个纯理论,纯概念,纯抽象的体系。数学中的数字,符号等等,在现实中是没有的,比如说,1,2,3是什么,你可以把它们写在纸上,但是他们什么都不是,然而他们又可以做很多事情,比如相加,相减等,而且实践证明,他们确实有用。抽象可以是多个层次的,比如,从苹果中抽象出数字(算术),再从算术抽象到代数,从一元的代数抽象到多元的代数等等。哲学是从所有的学科中得出一个适应所有学科的万能理论,所以哲学是对所有学科的抽象。而具体的学科中,又要对下一级的理论进行抽象,从而建立自己的理论空间。
先看三个核心概念:
封装的目的是隐藏内部实现细节,外部不用也不关心内部如何实现,只需要知道实体可以完成某种工作即可。比如,项目经理发现现在需要实现一个软件,软件的需求已知,他只需要告诉下一级的项目组长,我要一个软件,需求如何如何就可以了。至于开发细节,测试维护这些,他不需要关心。项目组长组织需要完成的模块等,然后交给具体的开发人员开发编码过程,编程技巧他不需要知道。封装就是为了各司其职 。
人们在使用一个工具的时候,越容易上手越好,比如你想自己用木头做一个小凳子,有这样一种“高级”锯子,功能非常强大,非常智能,只需要一些设置就可以自动锯木头,但是你需要学习十年才能真正使用。同时,有一种传统的锯子,拿来就可以使用,如果你不是一个变态到需要十几年时间来做一个小凳子的那种人的话,你几乎必然会选择后者,因为不需要学很多东西就可以使用。在计算机中也是如此,操作一个XML文件如果需要你去分析Linux系统引导流程的话,你肯定不会使用这种工具了。
封装主要为了方便使用者,我需要一个对象,它可以完成某种操作;你提供一个对象,它恰好能完成这种操作,就是这么简单。封装还提高了系统的可扩展性。一个封装良好的对象可以让其他的对象使用它而无需考虑任何内部的细节。这样的一个明显的好处就是,我们可以根据新的需求或者效率问题来改变对象封装的细节问题(比如一个更为高效的算法的使用等),而不影响使用该对象的代码。
继承的目的是最大限度的复用 ,实体之间往往具有非常相似的特征,但是又不完全一样。一个比较高的层次的抽象,可以进行适当的扩展以适应不同的需求。
多态是面向对象中的一个比较重要的概念。多态就是说,比较低层次的抽象的实体可以被支持比较高层次抽象的一些操作的实体所支持。这句话比较拗口,这样说吧,实现这些概念需要的工具,或者说描述方式,关于多态的描述有一个比较传统的例子:一个比较高层次的抽象,称为形状,然后从形状继承而来的有圆,长方形(也可以有由长方形继承而来正方形),三角形等低层次抽象,有这样一个外部实体,它支持对形状的操作,比如计算形状的面积(不用考虑怎么计算,也不用考虑计算的形状是那种),由于它支持的是形状(比较高层次的抽象),那么不论是圆,长方形,三角形它就都支持。而具体到每个低层次的抽象的实际计算时,各自使用各自的计算方法,最终返回结果就可以了。
这样做的一个明显的好处是,如果以后发现新的比较低层次的抽象后,直接加入系统即可,无需考虑上层使用者的修改。在运行时,编译器会知道需要计算哪个。
比如,上图所示的一个小例子,Stack(栈)为父类。intStack , charStack, strStack为子类,继承父类Stack
下面是一些面向对象的基本组件
类就是一组有共性的对象。它描述一个特定的抽象并提供创建对象的模板,如果类不具备某中特性,那么它的对象也不能具备该特性。比如人不会飞,那么,不管是John,Jack,还是Michael都不会飞(John,Jack and Michael都是对象,属于人类)
类就是分类,比如人类,就是关于人的定义,包括一些属性和操作这些属性的动作,是对一个实体的抽象的描述。因为是一个抽象概念,在使用的时候必须进行实例化,即将类具体为一个具体的实体。
对象,是一个类的具体实现,比如某人Jack,就是拥有一个名字属性为Jack的人类,他可以有其他的属性,具有人类里的所有方法(可以简单的理解为行为和操作属性的)。
对象需要有状态,用以描述对象的特征和当前的状态。对象还需要有行为,定义其他对象对该对象所施加的动作。行为可能需要依赖于对象的状态。在某种情况下,对象可以有某种行为,而在另外一些情况下,则不能。每一个对象都必须被唯一的标识(对象的名字不能相同,至少在一个场景中)。
对象通过协作,通信来完成一个比较完整的分工,这些分工整合起来,就是整个软件系统。
接口,举个例子来看,我们使用的键盘,我们只需要敲击键盘上的按键,A,B,C等,至于计算机如何解释这些按键操作,并将其解释成计算机能够识别的电路,我们不必关心。在人与计算机交互的过程中,通常,人与计算机的交互叫做接口,计算机与人的交互叫做界面,两者在计算机英语中都是Interface。
为什么说没有学过计算机的人反而容易理解面向对象呢?因为面向对象就是对实际社会的一种简单建模方式,而这种简单让程序员或者说计算机人不敢相信,而没有学过计算机的,可能会觉得这种建模方式跟自己的自然思维很相似,理解起来反而容易得多。这个不是我信口开河,是通过一段时间的观察而得出来的。
按照功能进行抽象,即可实现层次的分离
先说一下为什么要分层,分层是为了软件系统更容易开发和维护,如果所有的模块都挤在一起,或者各个模块的依赖性过高,那么,当程序出现问题或者系统升级以后,需要修改的地方就非常多,甚至会是整个工程,跟重新开发一次差不多。而分层以后,层次之间的耦合性比较低,修改一个层次(在接口不变的情况下),对其他的层次没有影响。这样就提高了开发和维护的效率,便于实现各司其职,互不干涉。当然,在实际的开发中,不能为了分层而分层,通常,三层的框架就比较合理而且好用了,MVC是一个比较常用的三层模型。
MVC模式,即模式(Model),视图(View),控制器(Controller)模式。模式就是些实体,比较通俗的说就是具体干某事的一些组件,比如软件公司的程序员们,需要有一定的编程技巧,懂得如何编写代码以实现功能。控制器就是业务流程,在什么情况下应该调用哪些组件来完成哪些功能。视图就是整个子系统与其他子系统或者用户交互的接口。
一般情况下,用户通过视图与系统进行交互,然后控制器解释这些请求,将能用到的模式即实体组织起来,完成用户的请求,然后再通过视图反馈给用户,整个流程就是这样。以信息管理系统为例:用户需要查询一些数据,输入查询条件,然后控制器负责解释需要在哪些对象中查找(这里的假设数据需要被封装到持久化对象中),然后持久化对象去数据库中检索满足条件的数据并存入对象,然后控制器将这些对象返回给视图,用户此时即可得到需要的结果了。
在J2EE中,M,V,C分别对应的实现方式为
使用JAVA语言进行开发,如数据库访问,XML文件操作等实体的操作对象
由JSP进行设计,即在HTML中加入JAVA代码,实现用户界面
由Servlet实现,主要用于JSP页面与模式(实体)进行数据交换等操作,起到一个桥梁作用。
下图为MVC模型的流程示意图
这一篇主要说说我对面向对象的理解和系统的分层,以后我们可以再讨论实际编程中的面向对象思想和分层的具体应用。