你真的懂面向对象了么?一篇文章带你全面领悟面向对象的思想

前言

一提到面向对象,那可是令我等小白闻之色变。不过,面向对象确实是现今我们程序员必须要了解并且能够熟练运用的思想,从C++语言开始,以及后面出现的C#、Java等主流编程语言都包含了面向对象这个重要的特性,现如今出现的新兴语言要是没有面向对象这个特性都不好意思说自己是语言了。
在这里插入图片描述
所以无论有多怕也要有直面深渊的勇气,奥里给!!!
废话不多说,让我们一起走进动物世界,哦不对,走进面向对象(~ ̄▽ ̄)~

计算机语言

无论学习什么,我们总要了解一下它的由来,它的发展历程。正如柏拉图提出的这个千古哲学命题“我是谁?我从哪里来?我要到哪里去?”。
在这里插入图片描述

了解面向对象之前总要先对计算机语言的发展有个大致的了解。计算机语言(Computer Language)指用于人与计算机之间通讯的语言,是人与计算机之间传递信息的媒介。其种类非常的多,总的来说目前可以分成机器语言,汇编语言,高级语言三大类。
第一代语言:机器语言,机器语言就是 0/1二进制 代码。因为计算机只能识别 0 和 1,所以无论是音乐还是文字都是以数字的形式存在于计算机中的。

第二代语言:汇编语言,天天面对0,1这样的数字进行编程对人来说是非常痛苦的,计算机的发展总是要越来越容易被人使用的,因此诞生了汇编语言。简单来说汇编语言就是将一串很枯燥无味的机器语言转化成一个英文单词,这方便了我们对程序的编写。

第三代语言:高级语言,高级语言的发展分为两个阶段,以 1980 年为分界线,前一阶段属于结构化语言或者称为面向过程的语言,后一阶段属于面向对象的语言。(关于面向对象语言发展史,这篇文章讲的比较详细,感兴趣的同学可以阅读——面向对象发展史
计算机语言发展关系如下图:
在这里插入图片描述
了解了计算机语言的发展,我们再来揭开面向对象的真面纱
在这里插入图片描述

面向对象是神马?

要想了解这个问题我们要先了解面向过程(Procedure Oriented),毕竟没有对比没有伤害(╯▔皿▔)╯。

面向过程

上世纪70年代的编程大都是是面向过程的,大名鼎鼎的C语言和Fortran语言就是面向过程的编程语言。
面向过程POP(Procedure Oriented Programming)是一种以过程为中心的编程思想。这些都是以什么正在发生为主要目标进行编程,不同于面向对象的是谁在受影响。与面向对象明显的不同就是封装、继承、类。 ——百度百科

面向过程按我的理解来说就好比你想拥有一辆五菱神车去秋名山,可又没钱,所以只能自己造。你需要先了解发动机是怎么工作的,方向盘是如何操控汽车转动的,然后自己来制造发动机、轮胎,等并给车喷漆、打光。
它是具体化的,流程化的,解决一个问题,你需要运用线性思维要一步一步的分析,一步一步的实现。解决简单的问题没有问题,若所思考的事需要很多人协作一起完成的话,用面向过程的思维思考很难完成。

正是因为面向过程线性思维的解决方法,它并不易于维护、复用、扩展 。所以
面向过程的思想随着程序规模的不断扩大,在60年代末期就已经出现了软件危机,在当时的程序设计模型中都无法克服错误随着代码的扩大而级数般的扩大,以至到了无法控制的地步,这个时候就出现了一种新的思考程序设计方式和程序设计模型-----面向对象程序设计

面向对象

先区分一下这几个首字母的意思,
OOA:Object Oriented Analyzing 面向对象分析
OOD:Object Oriented Design 面向对象设计
OOP:Object Oriented Programming 面向对象编程(或者有的就叫面向对象程序设计,也可以理解为编码实现)

面向对象OOP(Object Oriented Programming,面向对象编程)是一种对现实世界理解和抽象的方法,面向对象也可以简称为OO。最初面向对象是软件开发方法,现今其概念和应用已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。

最早的一门OOP语言是Ole-Johan Dahland和Kristen Nygaard发明的Simula(首先引入了类的概念和继承机制),没听过吧?我也没听过,但这并不妨碍它的后辈C++、Java等语言来折磨我们/(ㄒoㄒ)/~~。

面向对象编程能够提升程序的开发效率

在使用面向对象编程语言开发时,并不是所有的类都必须由程序员亲自编写。大部分的类都已经内置于面向对象编程语言中了,这些类可以为来自各个领域的程序员所用。通常将像这样的一组类(一组组件)称作“类库”。通过利用类库可以提升编程的效率。

我们接着用飙车的例子来比喻(可不能怨人爱飙车,毕竟那里老司机比较多( *︾▽︾)~~):如果此时我又想拥有一辆五菱神车去秋名山,由于认识了富婆,我除了自己造以外有了另一种选择,那就是直接去车店去买一辆,可以粗略地把它比作面向对象。
在这里插入图片描述
它是模型化的,你只需抽象出一个类,好比将现实的事物抽象出来,注意抽象这个词是重点,把现实生活的事物以及关系,抽象成类,通过继承,实现,组合的方式把万事万物都给容纳了。实现了对现实世界的抽象和数学建模。这是一次飞跃性的进步。

看到这你发现面向对象的优势了么?
一、你不需要在花精力再去了解如何制造车了,实现了高内聚低耦合
二、如果有一天车技高到可以不用方向盘了,想换车了,你只需要再去车店换辆拖拉机就行了,不需要自己再重新来造车了,提高了可维护性

可能这么说面向对象的可维护性和高内聚低耦合有点不好接受,再来举个例子加深理解。

高内聚低耦合

高内聚低耦合是我们每一个编程人员在编写程序时都一直追求的,其是软件工程中的概念,用来判断软件设计好坏的标准,主要用于程序的面向对象的设计,看类的内聚性是否高,耦合度是否低。目的是使程序模块的可重用性、移植性大大增强。
通俗来讲,将相关度比较高的部分尽可能的集中,不要分散就是高内聚,将两个相关的模块尽可以能把依赖的部分降低到最小,不要让两个系统产生强依赖就是低耦合。

高内聚
我们就以吃来举例,毕竟民以食为天哈。按我的理解高内聚就好比咱们吃货所热爱的麻辣烫、大杂烩。相比醋溜白菜、油麦菜那些一个个细分出来的名菜,大杂烩、麻辣烫就实现了高内聚,将大多数的蔬菜都集中在了一起,让想吃菜的朋友一次将各种蔬菜吃个够。
深夜放个毒,学习不易,偶尔放纵一下走样的身材也是可以的。
在这里插入图片描述
低耦合
耦合性(Coupling)也叫耦合度,是对模块间关联程度的度量,指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。
好比你经常吃的扬州蛋炒饭,它就耦合度很高,由于将”蛋”“饭”炒在一起,所以假如你又想吃茄子拌饭了,那将蛋炒饭中的鸡蛋换成茄子很困难,所以只能另炒一盘,想吃鸡蛋面也是同样的道理,所以扬州蛋炒饭是“高耦合的”,以至于”可维护性”比较差。

可维护性

众所周知,面向对象编程中,使用了一种称为“类”的要素,通过把若干个类组装到一起构建成一个完整的程序。从这一点来看,可以说类就是程序的组件(Component),面向对象编程的关键在于能否灵活地运用类。
这一个个类就好比一个个储物柜,当我们要存放或拿东西时,一般都是对其中的几个柜子进行更改,很少有对所有柜子的东西都进行操作的时候,所以不会影响其他的柜子。
在这里插入图片描述
而面向过程就好比把所有东西都放在一个大储物柜中,那么要拿东西时,我们就不得不将其中某一部分东西的摆放顺序打乱,这就有可能会引起柜子的其他东西不稳定,从而引发“BUG”。
所以从开发项目的角度来说,随着需求的不断变化,项目也是需要增加或删减功能的,而面向对象就能大大提高该项目的可维护性,不会因为一个“东西的拿放”,而引发无数的BUG。

对面向对象有了全面的了解后,你可能会发现有那么一丝丝熟悉感
在这里插入图片描述
没错,在我们的直觉中,大件物品都是由组件组装起来的。因此可以说面向对象编程方法把同样的直觉带给了计算机,创造了一种顺应人类思维习惯的开发方法。这也证实了计算机的发展趋势就是不断地拉近计算机和人的距离,使计算机成为更容易使用的机器。
在这里插入图片描述

面向对象与面向过程的区别

尽管已经对面向对象和面向过程有了大致的了解,我们还是要系统谈谈面向对象与面向过程的区别,从而加深自己的感悟。
依我的理解面向对象类似于高层组织的管理角色,面向过程则相当于专精于一个方面的工人。它们没有孰优孰劣之分,只是擅长的方向不一样。

一、解决问题方法不同
1、面向过程:一种以过程为中心的编程思想。是以什么正在发生为主要目标,一步步进行编程解决问题。
2、面向对象:是对现实世界理解和抽象的方法,以功能来划分问题,变为一个个类,而不是步骤。

二、中心不同
面向过程:程序设计方法以功能为中心来设计功能模块,难于维护;
面向对象:程序设计方法以数据为中心来描述系统,数据相对于功能而言具有较强的稳定性,因此更易于维护。

三、优势不同
1、面向过程:不支持丰富的“面向对象”特性(比如继承、多态),并且不允许混合持久化状态和域逻辑。
2、面向对象语言:在内部被表示为一个指向一组属性的指针。任何对这个对象的操作都会经过这个指针操作对象的属性和方法。

曾看过知乎一位大佬写的面向对象与面向过程区别的一篇文章,讲得可比咱这和尚念经生动形象多了——如何通俗易懂地举例说明「面向对象」和「面向过程」有什么区别?
在这里插入图片描述

面向对象的三大特性

想要理解面向对象的三个特性,那我们先要明确一下类和对象的关系,
对象:Object,含有“物体”的概念,一切皆物体(对象)。对象由静态的属性和动态的行为组成。是一个真实世界中的实体,对象与实体是一一对应关系的,意思就是现实世界的每一个实体都是一个对象,所以对象是一个具体的概念。

类:类就是具备某些共同特征的实体的集合,它是一种抽象的数据类型,一组具有相同属性和行为的对象的抽象。在面向对象的程序设计语言中,类是对一类“事物”的属性与行为的抽象。

类是对象的一个集合,对象是类的实例。
把类可以比作书本,那么对象就是《计算机编程的艺术》
(放图镇楼<( ̄︶ ̄)↗[GO!])。
在这里插入图片描述

1、封装
就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。一个类其实就是一个封装了数据以及操作这些数据的代码的逻辑实体。

2、继承
类是对对象的抽象,从OOD(面向对象设计)的角度考虑,继承是对某一批类的抽象.它可以让某个类型的对象获得另一个类型的对象的属性的方法。它支持按级分类的概念。提高代码复用性;继承是多态的前提。

3、多态
一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口,提高了程序的拓展性。

面向对象的五个基本原则

在程序设计领域, SOLID(单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)指代了面向对象编程和面向对象设计的五个基本原则。当这些原则被一起应用时,它们使得一个程序员开发一个容易进行软件维护和扩展的系统变得更加可能。SOLID所包含的原则是通过引发编程者进行软件源代码的代码重构进行软件的代码异味清扫,从而使得软件清晰可读以及可扩展时可以应用的指南。SOLID被典型的应用在测试驱动开发上,并且是敏捷开发以及自适应软件开发的基本原则的重要组成部分。——维基百科

首字母 指代 概念
S 单一功能原则SRP(Single Responsibility Principle) 认为对象应该仅具有一种单一功能的概念
O 开闭原则OCP(Open-Close Principle) 认为软件体应该是对于扩展开放的,但是对于修改封闭的概念
L 里氏替换原则LSP(the Liskov Substitution Principle LSP) 认为“程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的”概念
I 依赖倒置原则DIP(the Dependency Inversion Principle DIP) 认为“多个特定客户端接口要好于一个宽泛用途的接口”
D 接口分离原则ISP(the Interface Segregation Principle ISP) 认为一个方法应该遵从“依赖于抽象而不是一个实例”的概念依赖注入所以该原则的一种实现方式

面向对象的弊端

了解了面向对象的易维护、易复用、易扩展,是不是瞬间觉得面向对象才是真正的武林高手应该掌握的秘籍?那种万物皆对象、一切皆对象的思想令人想起来有点像古龙先生的“天下武功无坚不摧,唯快不破”,可谓最高境界。
在这里插入图片描述
诚然,面向对象程序设计是软件开发中一个非常有用的特性,但其本身并没有什么好坏之分(所谓缺点也只不过是它相对于其它事物不擅长的一面),只有用的好坏之分。《计算机是怎样跑起来的》中有句话就说的很好——“有一点希望诸位注意,那就是请不要把面向对象当成是一门学问。程序员是工程师,工程是一种亲身参与的活动而不是一门学问。请诸位把面向对象编程作为一种能提升编程效率、写出易于维护的代码的编程方法,在适当的场合实践面向对象编程,而不要被它各种各样的概念以及所谓的编程技巧所束缚。

巨大的类关系设计开销

我们看待一个事物应该用辩证的眼光去了解它(不是带哲学家,手动狗头),现在由于面向对象编程的理念越来越被人所接受,许多书籍都大肆宣扬它的好处,致使我等小白们过于疯狂,认为只要用了面向对象编程,就能得到非常容易维护、高内聚低耦合的代码。

其实不然,我们知道面向对象编程中,通过把若干个类组装到一起构建成一个完整的程序,因此也有人说这是“面向组件编程”。正是因为一个程序是由许多的类组成,在软件开发或者大型软件项目中,由于功能众多,所以类也特别多,若缺乏整体系统设计划分,易造成系统结构不合理、各部分关系失调等问题,这也就带来了巨大的类关系设计开销。
原谅是个小白,这个弊端我目前只大致了解,没有什么对它独特的见解,推荐一篇大佬的文章——关于巨大的类关系设计开销中具体有何弊端

性能比面向过程低

“程序的性能首先由编程语言的执行方式有关,其次才是设计范式”
因为面向对象是通过类对对象的抽象实现的,而类在调用时需要实例化,开销比较大,比较消耗资源。所以当性能是最大考量的时候,通常选用面向过程语言,比如单片机,嵌入式开发、Linux/Unix等一般采用面向过程开发。

数据结构耦合性极强

在面向对象重要的特性继承中,一旦父类中增加或删除某个字段,可能要影响到所有子类,影响到所有子类相关的逻辑。这显得非常不灵活,在一套复杂的继承体系中(像意大利面条那种),往父类中改变字段会变得越来越麻烦。
比方说BCD是A的子类,某天发现需要增加一个BC都有的数据,但是D没有,那么这个数据肯定不适合放到父类中,只能将BC抽象出来一个父类E,E继承于A,BC共有的字段加到E中。
一旦继承结构发生了变化,可能接口也要改变,比方说之前有个接口传入参数类型是E,当BC不再需要共用的那个字段,那么需要调整继承关系,让BC重新继承A,那么这个接口的传入参数类型需要改成A,其中的逻辑代码很可能也要发生调整。
所以面向对象在面对复杂的游戏逻辑变化时很无力,这也是为什么在大多数人在开发游戏时使用面向过程进行开发,游戏逻辑变化非常复杂和频繁,可能今天加了个字段,明天又删掉了,假如每次都要去调整继承结构,这简直就是噩梦,继承结构在面对频繁的数据结构调整时确实很无力。

初学者不容易接受!!!

在这里插入图片描述

区分“面向对象”和“基于对象”

经常有小白被这两个概念所困扰,其实区分起来很简单,面向对象的三大特点(封装,继承,多态)缺一不可。而“基于对象”是使用对象,但是无法利用现有的对象模板产生新的对象类型,继而产生新的对象,也就是说“基于对象”没有继承的特点。而“多态”表示为父类类型的子类对象实例,没有了继承的概念也就无从谈论“多态”。

很多流行技术都是基于对象的,它们使用一些封装好的对象,调用对象的方法,设置对象的属性。但是它们无法让程序员派生新对象类型。他们只能使用现有对象的方法和属性。所以当你判断一个新的技术是否是面向对象的时候,通常可以使用后两个特性来加以判断。“面向对象”和“基于对象”都实现了“封装”的概念,但是面向对象实现了“继承和多态”,而“基于对象”没有实现这些,的确很饶口。

最后再附上知乎一篇大神之作(小白慎入),怎么从本质上理解面向对象的编程思想?比起我这科普短文,这位大神写的可谓是刨根问底,令人茅塞顿开。文章内容牵强的地方欢迎指正。

猜你喜欢

转载自blog.csdn.net/weixin_45797022/article/details/105444791