这几天在调程序,所以想写写自己对“面向对象编程”的一些理解,希望对打算入门计算机编程的同志们有所帮助。之前,好几个师弟问过我,C++与C有什么区别,学习面向对象语言需要掌握哪些基础。另外,我在阅读一些程序时,发现有许多程序虽然是使用的C++,但程序结构仍没有摆脱C的思维。所以,在此我想系统地论述面向对象编程的思想以及学习面向对象编程应当掌握的一些知识。

在写之前,我要强调,面向对象是一种方法论,而不是一种具体的编程语言,所以在学习时千万不要过份纠结某特定编程语言的语法,而要去深刻理解背后的编程思想

为了比较好地对比面向对象和面向过程,文章里面的举例采用C++和C,但C#和JAVA是比C++更彻底的面向对象语言,如果没有学过c++的,建议直接学习C#或JAVA。

面向过程遇到的难题

面向过程,把问题世界看成步骤的逻辑组合。面向过程的编程往往会将一个问题分解成一个一个的步骤,这些步骤通过函数(function,C和C++)或方法(Method,JAVA)实现(后面统一采用“函数”的说法),然后再根据流程、逻辑将这些步骤进行组合(即按逻辑依次调用函数)。所以在编程之前,一般会画一些流程图,将步骤、流程、逻辑描述清楚,然后再开始写函数,最后写主程序调用这些函数。

我们先看一个问题:假设有一辆汽车,简化成一个质点,从A点直线运动到B点,AB的距离为500m,汽车要经历启动、加速、匀速、刹车等过程,汽车有最大速度,当加速到最大速度后匀速运动,刹车距离是20m,请用面向过程的方法来描述这个问题。

首先根据上面的问题描述设计一个流程图,

根据流程图我们知道需要实现start(),accelerate(),uniformMotion(),stop()等函数,同时我们还需要定义一些变量,如Vcurrent,Vmax,Lremain等等,再根据流程逻辑将这些函数进行组合

void main(){start();while(Vcurrent<Vmax) {accelerate();}while(Lremain<20){uniformMotion();}stop();
}

由此,我们会觉得面向过程的方法很简单,也很明了。但是如果把这个问题更复杂化一些:

汽车厂升级了该款汽车,加速度和最大速度都要都比改进的汽车大,厂家想通过一次比赛试验汽车的改进效果,于是改进前的汽车和改进后的汽车从A点同时出发,看哪一辆先到达B点。

此时要同时处理两辆汽车,要怎么办?是不是现在觉得用流程图去描述这个问题会变得费力多了,但也还勉强能实现,为B车重新设计一套类似的变量和函数即可。

但是问题如果更复杂呢?例如汽车增加新功能,可以转弯壁障,另外路上不只有这两辆车,还有公交车、货车、自行车、人,路面也不这么简单了,分成了私家车车道、公交车车道、非机动车车道和人行道,还有红绿灯、指示牌等交通管理设备,在这种情况下,如何用步骤去分解?简直要爆炸了!

由此可见,面向过程的抽象能力非常差,当问题复杂化后,面向过程的复杂度会成指数级上升,最后达到无法承受的程度。而且可以看到,面向过程的代码复用性也很差,只是函数级别的复用,这种强依赖于流程和逻辑的代码,耦合性极强,如果逻辑一变,就会有大量的代码需要修改,扩展性差、维护性差。

但是我们知道,计算机里面的虚拟世界越来越复杂,这是如何做到的呢?

既然面向过程遇到了难题,那么就放弃它,选择抽象能力更强、程度更高的编程思想——面向对象编程思想。

面向对象的哲学依据

既然面向对象是一种方法论,那么必然就有它的哲学依据。编程其实就是对现实世界的一种抽象,如何抽象现实世界,就必须要想清楚如何认知现实世界。

我们知道,本体哲学是讨论世界本源以及“存在”的哲学。关于“存在”的讨论,中国早在战国时期,名家公孙龙就提出了“白马非马”的论题,这个论题主要包含三层意思(参考冯友兰的《中国哲学简史》):
- “马”是一种动物,“白”是一种颜色,“白马”是一种动物加一种颜色。三者内涵不同,所以白马非马。
- “马”的外延包括一切马,不区别颜色,而“白马”的外延只有白马,有颜色区别,外延不同,所以白马非马。
- “马”这个共相是一切马的本质属性,而“白马”这个共相不同,所以白马非马。

总结起来,之所以白马非马,是因为“马”和“白马”是“名”(概念)而非“实”(实体)

公孙龙还有一篇《指物论》,指出了名与实的区别。公孙龙以“物”表示具体的个别的实体;以“指”表示抽象的共相(概念)。这里的共相就是指以某类事物共有的属性为内涵的名词。

到此,我们脑海里应该有了“概念”和“实体”的区别了。

希腊哲学家柏拉图也总结了类似的结论。柏拉图认为这个世界由两部分存在,一种是上帝创造的完美概念世界(形式世界,理型世界),这个概念就如同做饼干的模子,它是永恒不变的,另一个是实体世界,现实世界中的事物都是这些概念的映射,但这种映射并不是完美的,就如同用模子做饼干一样,饼干相似但并不完全一样。

但后来柏拉图的学生亚里士多德并不赞同他老师的观点,他并不认为现实世界来自于理型世界,也就是实体不是来自于概念,而恰好相反,概念来自于实体。他认为所谓形式只是某类事物共有的特征,所以用模子做饼干来比喻这个世界并不合适,他不赞同“概念”先于“实体”的说法。亚里士多德认为,我们所拥有的概念是透过我们感官感知到的事物而进入我们的意识形成的,而且我们拥有与生俱来的理性,可以组织所有的感官映像,并将它们加以整理和分类,所以才会产生诸如“植物”,“动物”,“人类”等概念

所以亚里士多德给了人类一个最伟大的发明——分类!他认为我们区别事物的方法就是将事物分门别类,于是他开始尝试对世界上的万事万物进行分类,并且是有层级的分类,他在最高层把世界分成两大类:生物和非生物。然后把生物又分成植物和动物,动物又分成禽兽和人类,就这么一步一步地分下去。亚里士多德的分类作品涉及到各种科学,其中很多名词成了现代科学中常见的词汇,因此他的分类学说对今天的科学仍然有极大的影响。

说到这,我们明白了概念和实体的区别,并且了明白了我们认识事物、区别事物的方法——分类,这便是面向对象编程的哲学依据。

面向对象里面的“类”即“概念”(也叫形式、理型、名、指等),对象即实体(也即具体的、个别的事物),那么程序组织的方法就是分类。

面向对象编程的核心思想

编程就是对现实世界的抽象,借助于上述哲学思想,面向对象编程定义了两个最核心的名词——类(class)和对象(object)。类实际上就是我们用来定义某个概念的,例如我们定义“Car”这么一个类,那么就在我们的程序世界里建立起了“Car”的概念。对象就是实体,例如我们用”Car”定义“myCar”这么一个对象,那么这个“myCar”就不再是一个概念了,而是一个独一无二的、具体的实体,“myCar”就是指我的那辆车,不再是别的车了。这个过程和柏拉图的理型世界有些相似,先有概念后有实体。

程序的组织方式就是分类,例如“汽车”可以分为“自动档汽车”和“手动档汽车”,那么我们继承”Car”,定义 “AutoCar” 和 “ManuCar” 两个类。这里提到了继承这个概念,待会还要详说。

那么,通过什么来描述一个概念(类)呢?换而言之,如何定义一个类呢?

首先,任何事物都会有它的属性。例如一个人,他(她)有区别与动物的社会属性、劳动属性等等,另外还会有身高、体重、年龄、性别、户籍、民族等等属性。属性描述了事物的静态特性,除此之外还需要描述事物的行为特性,这就是函数。函数(function)有功能的意思,它意味着类能完成一些功能。例如人能走路、说话、跳舞,这些就是人的行为特性,我们用函数来实现。从外界来看,人能完成走路、说话、跳舞等功能,假设这个人是机器人,那么外界就可以请求该机器人完成这些功能。简而言之,我们通过属性(C++中叫变量)和函数定义类

回到之前的汽车问题:假设有一辆汽车,简化成一个质点,从A点直线运动到B点,AB的距离为500m,汽车要经历启动、加速、匀速、刹车等过程,汽车有最大速度,当加速到最大速度后匀速运动,刹车距离是20m,请用面向对象的方法来描述这个问题。

这个问题中仅涉及到一个概念,即“汽车”,所以定义一个”Car”的类,描述“Car“的属性主要有m_currVilocity(当前速度),m_maxVilocity(最大速度),m_currDistance(当前行驶距离);描述“Car“的功能行为主要有start(),accelerate(),uniformMotion(),stop()等。根据这些,我们定义”Car”这个类:

class Car{private:float m_currVilocity, m_maxVilocity, m_currDistance;public:void start();void accelerate();void uniformMotion();void stop(); float getCurrVilocity();float getMaxVilocity();float getCurrDistance();
}

再通过”Car”这个类定义一个对象”myCar”(实体),就可以对这个对象进行操作了。

void main(){Car myCar;myCar.start();while(myCar.getCurrVilocity()<myCar.getMaxVilocity()) {myCar.accelerate();}while(500- myCar.getCurrDistance()<20){myCar.uniformMotion();}myCar.stop();
}

到此,我们已经完成了简单的面向对象的编程,这时候初学者一定会纳闷,这样搞比面向过程的方法麻烦多了,有什么好的。这个问题暂时搁置一下,先看看面向对象的三个基本特性。

面向对象的三个基本特性

面向对象的三个基本特性分别是封装性、继承性、多态性。

(1)封装性。比较细心的同志会发现我在定义属性的时候使用了”private”这个关键词,这表示这些属性不能被外界直接访问和修改,这就是封装性。之所以这么做,是为了隐藏复杂性,包括两方面的原因:一是对客户隐藏他们不需要知道的细节,让客户专注到如何使用接口上来,这也可以防止他们窥探类的内部设计思想;二是允许库设计人员修改内部结构,不用担心它会对客户程序员造成什么影响,只要对外接口不变,类的内部变化不会对外界造成任何影响。所以封装让类保持了一定的独立性,有利于设计高内聚、低耦合的程序。

(2)继承性。上面提到了 “汽车”可以分为“自动档汽车”和“手动档汽车”,所以能继承”Car”,定义 “AutoCar” 和 “ManuCar” 两个类,这就是继承性。 这里,”Car”被称为父类,而 “AutoCar” 和 “ManuCar” 称为子类,子类会继承父类的所有属性和函数,所以 “AutoCar” 和 “ManuCar” 就复用了”Car”的所有属性和功能。但继承不止有类的继承的这种方式,在JAVA里面还有接口的继承,这个留给“JAVA与C++的区别”这篇文章。另外,继承性也不仅仅只体现在继承上,还可以通过“组合”来实现,而且有时候使用组合会带来更好的程序结构,这个也留到以后再写。

(3)多态性。“AutoCar” 和 “ManuCar” 两个类纯粹继承”Car”还不够,否则就和”Car”一模一样了。我们知道,自动档汽车和手动档汽车主要区别在于加速的方式不一样,所以就要重写accelerate()这个函数,通过重写,“AutoCar”定义的对象和 “ManuCar”定义的对象再加速时就会呈现不同的结果,这就是多态性。程序的多态性还包括“函数重载”,在C++里面,允许存在多个同名函数,但是参数表不同,或者参数类型不同,或者两者都不同,这就是“函数重载”。

了解了三个基本特性后,再回到之前那个问题,很多新学面向对象语言的人都会觉得面向对象要比面向过程麻烦多了,我当初也是这种感觉。但是当问题复杂一些时:汽车厂升级了该款汽车,加速度和最大速度都要都比改进的汽车大,厂家想通过一次比赛试验汽车的改进效果,于是改进前的汽车和改进后的汽车从A点同时出发,看哪一辆先到达B点。

用面向对象的方法轻而易举就完成了,我通过Car 定义两个对象就行了,一个是改进前的car1,一个是改进后的car2,这两个Car只是属性有所不同而已,然后就可以对这两个Car进行操作了。

更复杂的情况:汽车增加新功能,可以转弯壁障,另外路上不只有这两辆车,还有公交车、货车、自行车,路面也不这么简单了,分成了私家车车道、公交车车道、非机动车车道和人行道,还有红绿灯、指示牌等交通管理设备。

公交车、货车、自行车等都是交通工具,定义一个交通工具的类,找到他们的共有属性和行为,在交通工具这个类中定义,然后再继承交通工具的类,定义公交车、货车、自行车,找到他们的不同点,添加各自独特的属性和函数,改写与父类有区别的。然后再定义公路、交通管理设备等类,完成这些类的定以后,再根据实际情况定义不同的对象,对象之间进行交互,就能把这个复杂的问题给解决了,这就是封装性、继承性、多态性带来的神奇力量!

前面主要介绍了面向对象编程的哲学依据、核心思想以及基本特性。下面介绍入门面向对象编程要学习掌握的一些基础知识,并且推荐几本书。

统一建模语言(UML)

写程序离不开开设计,就如同建房子,谁都不会一上来就开始垒砖头。在建房子之前,设计师得先根据客户的需求设计房子的结构、功能、布局等等,并且要绘制标准的图纸,这样建筑工人才能拿着图纸开始建房子。编程也一样,首先得分析客户的需求,然后再根据需求设计软件的构架,这个构架需要使用标准的语言进行描述,这就像标准的图纸,如此程序员才能根据图纸往上垒代码。

这个标准语言就叫做统一建模语言(Unified Modeling Language,UML),它是用来对软件密集系统进行描述、构造、视化和文档编制的一种语言。简单一点理解,它就是软件的图纸。

UML包括的内容非常多,这里仅简单地介绍一下。前面说了第一步要对客户需求进行分析,UML提供了用例图,用于软件的功能需求分析。用例图主要包含两个元素——参与者和用例。参与者用小人表示,用例用椭圆表示,参与者和执行者之间的关系用箭头表示。用例图主要反映了从参与者的角度看,系统应当向外暴露哪些功能,并不反映系统内部的结构。进行用例分析时,最重要地就是区分参与者和用例。

除了要进行功能需求分析外,还要对系统的动态行为需求进行分析,UML提供了“活动图”,活动图中最重要的两个元素是甬道和活动,甬道往往用于表示实际执行活动的对象,活动的执行有一定的流程和逻辑,这类似于面向过程编程中的流程图。

完成需求分析后就开始设计软件的构架,这包括静态模型和动态行为,静态模型包括组建图、类图等等,动态行为包括状态图、时序图等等。程序员要和这些图直接打交道,掌握它们很重要。但这些图比较复杂,不是三言两语就能说的清的,请阅读相关的书籍。

设计模式

软件设计最核心的一个理念是区分“不变”与“变化”,这样会有利于软件后期的维护和升级。“变化”就是意味着将来可能需要修改或者升级,在设计时一定要将这部分程序小心地隔离出来。将来要修改这部分程序时不用担心牵一发而动全身,这样的软件才叫做构架良好的软件。

区分并隔离“不变”与“变化”是需要大量的编程经验的,对于新手而言,在没有经验积累的基础上,如何才能设计架构良好的软件?这并不是做不到的,因为前人已经为我们总结了大量的经验,并归纳出许多设计原则,这就是设计模式。

这些设计模式就是代码模板,我们可以直接拿来用,我们要做的只是将我们的具体问题套进去就行了。设计模式也许多种,建议阅读相关书籍进行学习。

书籍推荐

这里仅根据我自己的学习过程推荐书籍,但不一定是最好的书籍,也不一定适合所有人,还请根据实际情况选择。

  • (1)谭浩强,清华大学出版社,《c++程序设计》

  • (2)侯俊杰,华中科技大学出版社,《深入浅出MFC》

  • (3)Bruce Eckel,机械工业出版社,《java编程思想》

  • (4)Eric Freeman,东南大学出版社,《深入浅出设计模式》

  • (5)谭云杰,水利水电出版社,《大象-Thinking in UML(第二版)》

在学习编程时,建议精读,不要坐这山望那山,所以就推荐这几本了。

面向对象编程思想及入门知识相关推荐

  1. 我们程序员眼中的对象-看完华华的文章妈妈再也不担心我找不到对象了-Java面向对象编程思想理解系列(1)

    大家好!!!我叫霜华,是一个帅到没天理 骚里骚气,热爱人生与绿色 是一个不像程序员的程序员 作为一个在java这门领域沉迷一年半的人,如今在技术栈广度和深度 还是业务能力在客观层面讲确实进步迅速 (可 ...

  2. CoreJava学习第五课 --- 进入第二阶段:面向对象编程思想

    面向对象编程思想 1.面向过程 ​ 从计算机执行角度出发 ,代码执行过程核心为从程序的运行过程出发,构建编程思路,例: 哥德巴赫猜想 // 面向过程1 用户输入一个数n2 验证数字的正确性2.1 正确 ...

  3. 面向对象编程思想概览(一)类和对象

    一.简介 生活中有许多技巧值得我们在程序设计中模仿和借鉴.采用类的思想可以方便地把具有相同属性和方法归为一类,从而简化编程.本讲利用生活的常见示例,浅显通俗的语言,生动地介绍了面向对象编程中类与对象的 ...

  4. python完全支持面向对象编程思想_面向对象的编程思想和Python的类,访问和属性,继承...

    本文将从访问限制,属性,继承,方法重写这几个方面继续介绍面向对象的编程思想和Python类的继承. 复制代码 一.访问权限: Python中在类的内部定义属性和方法,在类的外部是可以直接调用或进行访问 ...

  5. 面向对象编程思想概览(三)继承

    一.简介 本讲以大家耳熟能详的<西游记>中的唐僧师徒四人为例,介绍了类的继承的概念和实现方法,总结了继承的特性和优点,帮助同学们理解面向对象编程中继承的用法,进而掌握面向对象程序设计的基本 ...

  6. 面向对象编程思想 以及类与对象

    一.面向对象编程思想 众所周知,我们常见的编程思想有面向过程和面向对象两种,像我们最基础的c语言,就是一种以过程为中心的编程思想,不关注具体的事件和对象而是针对于解决问题的思路和目标,这种编程思想由于 ...

  7. 基于STM32F103移植华为LiteOS_任务挂起与恢复_面向对象编程思想之按键状态机

    华为LiteOS_任务挂起与恢复_面向对象编程思想之按键状态机 因为在做华为LiteOS任务挂起和恢复需要使用到按键去触发任务挂起和恢复动作,因为我就萌发出使用状态机这种架构做一个按键检测触发.回想已 ...

  8. 第三章面向对象编程思想

    """ 编码规范: 类名首字母应以大写字母开头 类的成员/属性: 成员属性 实例化对象的属性: self.name=name 可以被访问,也可以在类的外部被修改 私有属性 ...

  9. Java学习——面向对象编程思想

    目录 一. 基本概念 二. 面向对象与面向过程的区别 三. 面向对象程序设计的类与对象 3.1 对象 3.2 类 四. 面向对象的四大特征 4.1 抽象 4.2 继承 4.3 封装 4.4 多态 1) ...

最新文章

  1. 最近要换个主机,现在的太慢了
  2. hpuoj--1093: 回文数(一)
  3. 网络游戏外挂核心封包揭密
  4. Matlab高级绘图功能
  5. LeetCode 20. Valid Parentheses(c++)
  6. gabor变换人脸识别的python实现,att_faces数据集平均识别率99%
  7. SqlServer数据库字典
  8. java main usage_java-一个简单的访问DB的main方法使用 | 学步园
  9. 性能测试常用的linux命令
  10. 怎么隐藏splitcontainer分界线
  11. 文件相似度比对工具的设计与实现
  12. 了解最新升级手持式频谱仪版本和各项性能
  13. Mysql开启局域网连接
  14. 如何在iOS上创建矢量图形
  15. Minecraft整合包 [GTNH]格雷科技:新视野 服务器搭建教程
  16. 贝多芬第九交响曲,卡拉扬63年版,第四乐章演唱部分歌词文件
  17. Java用POI导出Excel合并单元格、字体、居中样式
  18. TCP/IP基础(三)
  19. C语言中函数参数传递的方式:值传递,地址传递
  20. 考虑下面两个JSP文件代码片断: test1.jsp:

热门文章

  1. 计算机导论基础论文3000字,计算机导论课论文3000字(2)
  2. RADIDE MultiPaste
  3. 网传锐捷将与wifi共享精灵携手定制校园专版
  4. 【C++编程语言】之C++对象模型和this指针
  5. Linux Xshell连接不到虚拟机 -- Could not connect to ‘xxx.xxx.xxx.xxx‘ (port 22): Connection failed
  6. 电商平台-库存管理设计与架构
  7. 游戏开发完整流程之美
  8. s2020gc53拼数游戏
  9. pyqt5 多线程遇到的问题总结
  10. python画雪人怎么弄_在HTML中使用css3实现雪人动画效果