首页 程序笔记 工作这么多年,你能向新人解释清到底什么是面向对象编程吗?

工作这么多年,你能向新人解释清到底什么是面向对象编程吗?

面向对象编程想必大家都耳熟能详,但是写了这么多代码你对面向对象有清晰的认识吗?

来看看这几个问题:

  • 到底什么是面向对象编程?

  • 和面向过程编程有什么区别?

  • 什么又称为面向对象语言、面向过程语言?

  • 用面向对象语言写的代码就面向对象了?

  • 面向对象编程真的就这么好吗?

  • 复杂的业务用面向对象编程就合适了吗?

我还真没具体地定义过到底什么是面向对象编程。

所以假设有人问到底什么是面向对象编程?有什么好处?

一时还真不知道怎么说,或者说成体系的解释。

这篇文章我就谈谈我的理解,也试着看能不能说清啥叫面向对象编程。

正文

从二进制命令到汇编语言。

从汇编语言到面向过程语言再到面向对象语言。

计算机语言的发展是为了便于人类的使用,使其更符合人类的思考方式。

计算机的思路就是取指执行,一条直道走到底,它可不会管你什么抽象,不管什么业务建模,通通得给它变成一条条指令,排好顺序让它执行。

而我们人类不一样,我们的思维在简单场景来看是一条道,但在复杂场景就需要做各种分类,才能理清楚关系,处理好事务。

就像法庭,分为法官、书记员、法警、原告、被告、证人等角色。

这么多人分好类,按照法庭审理各司其职,一个案子才能高效、顺利得审判。

再回到计算机语言来,汇编我就不说了,面向过程其实就是一条道的思路,因为起初就是按计算机的思路来编写程序。

我就拿用咖啡机煮咖啡为例,按照面向过程的流程是:

  1. 执行加咖啡豆方法

  2. 执行加水方法

  3. 执行煮咖啡方法

  4. 执行喝咖啡方法

很简单直观的操作,你可能没什么感觉,我再按面向对象思想来分析下这个流程。

在执行煮咖啡操作前要抽象出:人和咖啡机(分类),然后开始执行:

  1. 人.加咖啡豆

  2. 人.加水

  3. 咖啡机.煮

  4. 人.喝咖啡

是不是有点感觉了?

面向过程,从名字可以得知重点是过程,而面向对象的重点是对象。

从这个例子可以看出两者的不同:面向过程是很直接的思维,一步步的执行,一条道走到底。

而面向对象是先抽象,把事物分类得到不同的类,划分每个类的职责,暴露出每个类所能执行的动作,然后按逻辑执行时调用每个类的方法即可,不关心内部的逻辑。

从例子可以看出面向对象编程执行的步骤没有变少,整体执行流程还是一样的,都是先加咖啡豆、加水、煮咖啡、喝,这个逻辑没有变。

无非就是划分了类,把每一步骤具体的实现封装了起来,散布在不同的类中。

对我们程序员来说是最最直接的感受:变的其实就是代码的分布,煮咖啡的代码实现被封装在咖啡机内部,喝咖啡的代码实现被封装在人内部,而不是在一个方法中写出来。

代码的分布确实是最直观的,但是变得不仅只是分布,而是思想上的变化。

就是上面提到的计算机思维到人类思维的变化。

我认为这个变化是因为软件的发展,业务越来越复杂。

人们用面向过程语言编写复杂的软件时,需要按照不同的功能把一些数据和函数放到不同的文件中,渐渐地人们就发现这不就是先分类吗?

并且好像业务分析下来都能和现实世界的东西对应上?

于是人们慢慢地总结、提炼就演变成了面向对象,再根据面向对象的特性提炼出关键点:封装、继承和多态。

而这个面向对象思想就类似我们人类面对复杂场景时候的分析思维:归类、汇总。

所以面向对象编程就成为了现在主流的编程风格,因为符合人类的思考方式。

面向过程编程和面向对象编程从思想上的变化是:从计算机思维转变成了人类的思维来编写编码。

所以我们知道面向对象编程其实是一种进步,一种更贴近人类思考方式的编码风格,是源于人们用面向过程编程时的经验总结。

至此我们知道了面向对象编程的来源,相信知晓了来源能更好的理解面向对象。

那到底什么是面向对象编程?

面向对象编程(Object Oriented Programming,OOP)是一种编程范式或者说编程风格。

学术一点讲就是把类或对象作为基本单元来组织代码,并且运用提炼出的:封装、继承和多态来作为代码设计指导。

这其实就是面向对象编程。

其实从上面煮咖啡的流程应该能 get 到这个含义了。

OOP 说白了就是拿到需求开始分析,进行抽象建立业务模型,每个模型建立对应的类。

思考业务的交互,根据交互定义好接口并做好接口的控制访问,将于此类相关的数据和动作都封装起来。

抽象出父类,子类继承父类来进行代码的复用和扩展。

执行功能时用父类来调用,在实际代码运行过程会进行动态绑定,调用子类的实现达到多态的特性。

多态,学术点讲就是:运行时用相同的代码根据不同类型的实例呈现出不同行为的现象。

如果有新功能要实现,只需要创建一个新子类,以前的执行逻辑不需要发生变化,这就是「开闭原则」,对修改关闭,对扩展开放”。

来简单的看个代码可能会有更直观的感受,没记错的话大学时也是拿动物举例。

狗是动物、鸭子是动物,所以有个 Animal 类。

然后能发声,所以有 voice 方法。

    public class Animal {       public void voice(){           System.out.println("动物的叫声");       }     }

然后搞个 Dog、Duck 继承 Animal 实现各自的 voice。

    public class Dog extends Animal {       public void voice(){          System.out.println("汪汪汪~");       }     }    public  class Duck extends Animal {       public void voice(){          System.out.println("gagaga~");       }     }

然后到时候就可以实例化不同的对象来达到多态的效果。

    public class Test{       private Animal animal;       public void setAnimal(Animal animal) {         this.animal = animal;       }       public void voice(){           animal.voice();       }     }

多态带来的好处,无非就是 Test 里面代码不用动,你想要狗叫你就 new Dog 然后 set 进去,如果要鸭子就  new Duck 然后 set 进去。

如果加入了新动物那就建一个新动物类 set 进去就行,符合开闭原则。

和面向过程编程有什么区别?

其实从上面煮咖啡和动物的这两个例子应该能感受出来区别。

最重要的是思想上的区别,上面也已经提到了。

还有一点就是数据和动作。

面向过程编程这种编程风格是以过程作为基本单元来组织代码的,过程其实就是动作,对应到代码中来就是函数,面向过程中函数和数据是分离的,数据其实就是成员变量。

而面向对象编程的类中数据和动作是在一起的,这也是两者的一个显著的区别。

什么又称为面向对象语言、面向过程语言

面向对象语言其实就是有现成的语法机制来支持类、对象的语言,比如 Java。

当然还要有支持继承、多态的语法机制。

面向过程语言就反着理解,没有现成的语法机制来支持类、对象等基本单元来组织代码。

当然不是你用了面向对象语言写出来的代码就面向对象了。

你要通篇就一个 class,一堆杂乱无章都往里面塞,不归类、没有封装的意识,一条直到,这可不叫面向对象编程。

当然也不是用面向过程语言就写不出面向对象的代码,只是由于语法层面的不支持,写起来没那么方便,需要用一些手段,具体就不展开了。

所以语言只是为了更好的支持编程范式,重要的还是思想上的转变。

面向对象编程真的就这么好吗?

结论先上:软件设计没有银弹,没有最好的,只有合适的。

前面也提到了面向对象更符合人类的思考方式,这其实就是优势,能 hold 住复杂的需求。

复杂的需求关系都是错综复杂的,我们分类、抽象、封装就能得到一个个规范化的模块(类)。

大型项目都需要很多人协同合作,因为划分的清晰,每个人只要实现自己负责的模块。

然后根据模块之间关系再组装起来即可。

脉络清晰也使得我们开发的时候思路也异常的清晰,提升开发的效率。

并且由于封装的特性,类的内部是高度内聚的,会利用访问控制权限暴露出有限的访问,这使得类内部的数据不会被随意更改,提高代码的维护性。

还有前面提到的继承,提高代码的复用性,由继承实现的多态也符合开闭原则。

我还看过一个很形象的解释(很久之前看过,忘了出处),说面向过程是蛋炒饭、面向对象是盖浇饭。

蛋炒饭混合在一起,盖浇饭是分层的,如果不要葱,盖浇饭把上面的菜拨了直接换个没葱的菜,蛋炒饭就难搞了,得重新炒一份。

其实这个比喻体现的思想就是面向对象可维护性比较高,而且可以重用,更加灵活。

而面向过程就不易维护,不易扩展。

这个比喻没错,上面的说法也没错,但是我觉得需要加个前提:在合适的场景。

虽说我上面列了很多面向对象编程的优点,但是软件设计没有银弹,没有最好的,只有合适的。

当你做一个很简单的玩意,比如简易计算器,你抽象来抽象去其实意义不大,直接按照面向过程的设计一条道走到底才是最合适的。

就像我们平日里面写代码,是否遇到个情况:为了一个功能需要新建一个类,然后类里面就一个方法。

因为按照面向对象的思维,这个是需要抽象的。

然后为了复用还做了继承、预留了一些接口等等,就想着以后扩展。

可能过了很多年到这个项目扑街了,都没扩展上。

在项目里很多地方都做了这样的钩子,都白费,没鱼儿上钩。

还不如当时就直来直往的写,绕来绕去的新同事进来看的都一脸懵逼。

有些人说代码就是得这样写,就是要为了之后的扩展,设计模式上!

扪心自问一下,有多少之后用上了?

所以有很多大牛在那里骂:

“面向对象编程是一个极其糟糕的主意,只有硅谷里的人能干出这种事情。” — Edsger Dijkstra(图灵奖获得者)

“有时,优雅的实现只需要一个函数。不是一个方法。不是一个类,不是一个框架。只是一个方法。” — John Carmack(id Software的创始人、第一人称射击游戏之父)

“面向对象编程语言的问题在于,它总是附带着所有它需要的隐含环境。你想要一个香蕉,但得到的却是一个大猩猩拿着香蕉,而其还有整个丛林。” — Joe Armstrong(Erlang语言发明人)

还有挺多,我就不列出来了。

确实有时候写代码的时候能明显感觉到有时候需要的只是一个函数。

所以对于那些:别问,问就是面向对象,还有一些大肆鼓吹设计模式的:别问,问就是设计模式 的人而言,我是不认可的。

还是那句:

软件设计没有银弹,没有最好的,只有合适的。

复杂的业务用面向对象编程就合适了吗?

一般的说法是面向对象适合复杂的场景,这句话其实也不全对。

当时我在打 LOL 的时候就在感叹,这技能的释放,然后又因为加了 buf 可能有什么特别的计算,每一次版本变更好像改的东西挺多啊。

就像亚索出来的时候,这狂风绝息斩对石头人的大没用,被蝎子拉了也没用,这不是得做很多判断啊。

每新出一个英雄,new 一个对象,其他英雄对象都得改啊,因为新英雄针对不同英雄可能有不一样的伤害效果。

总之我觉得很复杂,每一次改动会涉及很多很多,所以脑子里面就有疑问这是怎么做的,面向对象的话得改好多呀。

前几天我看到了知乎 invalid s 的回答,给我解了惑。

原来复杂的业务用面向对象编程还真不一定合适。

他举的是 WOW 的例子,虽说我不知道 LOL 是不是这样做的,但是这不重要。

他让我知道在这个场景里面如果是以面向对象来设计,那面对如此繁多的职业、种族和技能,在频繁地版本迭代下是招架不住的。

所以从中可以看到不是面对复杂的场景就直接上面向对象的,还是得具体情况具体分析,面向对象不是万能的。

最后

其实我还看到有人说面向对象的本质是对真实世界的映射,这还真不一定。

我们平日写代码能很明显地感受到有时候就是为了抽象和复用搞了一个类。

而且很多情况抽象出来的类和现实对应不上,反正就是为了需求而造的一个类。

网上也看到很多言论,说啥 OOP 就是错的、或者说 OOP 就是对的。

我觉得都很极端,还是那句软件设计没有银弹,没有最好的,只有合适的。

关于面向对象还想提一下。

在去年我写 Fork/Join 的时候提到了分而治之。

面向对象其实也有这味道。

拿古代举例,皇帝其实不知道具体治理细节,也不用管理具体细节,一个国家这么大的庞然大物抽象了很多事务,分成了很多类官员。

然后将每类官员需要做的事情封装好,让每类官员各司其职。

皇帝只需要统筹全局,根据每类官员的职责颁发不同的任务即可,不需要关心他具体是如何实施的。

皇帝只要说,让各县都推行啥啥啥,即可。

其实等于只要招呼县令这个类去做事情,管你哪个县,皇帝不需要关心。

然后每个县令得到相同的命令,但是会有各自的治理方法,这其实就是多态。

这其实就是面向对象的思想。

好了,说了这么多不知道能不能讲清什么叫面向对象,如果不清晰的话还望见谅,毕竟能力有限。

我再稍微的总结一下面向对象编程:

OOP 其实就是一种编程范式或者说风格,是一种以类或对象为单元来组织代码的编码方式,让代码高内聚,低耦合。

OO 符合人类面对复杂事物时思考方式,抽象、建模、分类、归类。

最后,欢迎加我好友进行深入地交流,备注「进群」,拉你进交流&内推群。

平日的面试题遇到难处,或者看某个知识点翻遍全网的资料还是感觉很模糊、不透彻,可以私聊我,给我留言。

遇到合适的我会整理写出一篇文章,我不会的去请教别人也给整出来。

那种工作遇到很细节的场景的还是别了,这种问你上司比较合适

站心网

面向对象编程想必大家都耳熟能详,但是写了这么多代码你对面向对象有清晰的认识吗?来看看这几个问题:到底..

为您推荐

如何更有策略的选择工作,让自己少奋斗10年?

你好,我是粥左罗,今天我们聊的话题是,职场中的一个个选择,是如何影响你的整个职业生涯的。职场上工作两三年以上的朋友可能都有过这样的感受:感觉 XXX 也没有比我强很多啊,为什么他能赚这么多?其实这不是主观..

程序员失业日记1:工作五年,交接半天

最近发现越来越多的小伙伴被公司裁员,有的是因为公司业绩不景气被裁员,有的是因为压力太大离职。很多公司都在裁人、减员。找工作也比之前难。刚好去年我也被上家裁员了,正好做一个系列的日志,希望能帮到在找工作..

2018总结:理财、工作、生活

18年应该是工作10年来最惨的一年,主要是在这个特殊的一年开始理财了,损失惨重。工作上还是进展挺大了,去了一个都是老同事的公司,工作内容也有了新的挑战。理财说起来一把泪的18年理财。有句话说得好,去年不炒房..

关于工作和成长,这是我的121条具体建议

关于面对批评01.没有人对被批评感到高兴。如果有,TA撒谎。02.面对批评,得体的第一反应是“不急于解释,不反唇相讥”。03.每天,或者最长每周养成习惯,把自己存在的问题和造成的麻烦用最重的形容词想一遍,诚实的..

【深度思考】一线开发对于工作的感悟分享

前言写在前面的是关于自己的一点介绍,21年本科毕业,学的是计算机科学与技术专业,到现在工作(实习)也有两三年了。自己本身对编程和开发是有兴趣的,同时也对项目管理、团队建设方面有兴趣。目前有两段工作经历,..

JS 中彻底删除 JSON 对象组成的数组中的元素

在 JS 中,对于某个由 JSON 对象组成的数组,例如:var test = [{ "a": "1", "b": "2" }, { "a": "3", "b": "4" }, { "a": "5", "b": "6" }];如果我们想要删除其中的第二个json对象,应该怎么做呢?其实方法和操作数..

累死你的不是工作,而是工作方式

《浪潮之巅》的作者吴军,在《得到》专栏里,提及了Google刚进中国时候的一件事。刚开始,Google总部对中国研发团队的评价非常低,因为“出工不出活儿”,北京的三四个工程师都抵不上Google总部的一个工程师。后来吴..

工作中人们常提到的数据预处理,说的到底是什么?

数据预处理一方面是为了提高数据的质量,另一方面也是为了适应所做数据分析的软件或者方法。在做数据分析时,我想许多数据分析师会像《R语言实战第二版》的作者卡巴科弗那样发出感叹:“数据分析师在数据预处理上花..

.NET 主程序的.dll.config文件有什么用?

.dll.config 文件通常是在开发过程中自动生成的,它的主要作用是为某个类库(.dll 文件)提供一个独立的配置文件,以便开发者可以为该类库单独定义或测试配置项。以下是生成 .dll.config 文件的原因和机制:1. 配置..

CPU、GPU 和 TPU 之间有什么区别?

什么是 CPU、GPU 和 TPU?它们都是用于计算任务的处理器芯片。可以把你的大脑想象成一台计算机,能够完成诸如阅读书籍或解决数学问题的任务。每一项活动都类似于一个计算任务。例如,当你用手机拍照、发送短信或打开..

.NET9 F#有什么新特性?

F# 9 的新特性简介F# 9 是 .NET 9 的一部分,带来了多项增强功能,旨在提升开发效率和语言特性的一致性。这些改进不仅为现有的 F# 开发者提供了更强大的工具,也使新手更容易上手。以下是主要特性概览:1. 改进的类..

尤雨溪的VoidZero到底是什么?

尤雨溪创立的VoidZero是一家致力于打造下一代JavaScript工具链的公司,其核心目的是解决当前JavaScript开发工具在性能和效率上的痛点。这个工具链的目标包括提高速度、减少重复处理,并用统一的架构简化开发者的操作..

什么是.NET渐进式Web应用(PWA)

.NET 渐进式 Web 应用(PWA,Progressive Web Apps)是一种结合了 Web 应用的跨平台性和本地应用体验的应用程序。通过 PWA 技术,.NET 开发者可以使用 Blazor 和 WebAssembly 创建 Web 应用,支持在各种设备和操作系..

.NET9在ASP.NET MVC有什么更新?

在.NET 9 中,ASP.NET Core MVC 和其他 ASP.NET 功能进行了多项增强,以改善开发者的体验和应用性能:静态文件处理和缓存优化:ASP.NET Core MVC 现在支持静态文件的“指纹化”处理,发布时会生成包含唯一..

.NET框架和CLR的工作原理?

.NET 框架和 CLR(公共语言运行时,Common Language Runtime)共同构成了一个应用程序运行和开发的环境,为多种编程语言提供跨平台支持、内存管理、异常处理、安全性、以及其他服务。它们各自的工作原理如下:.NET F..

技术实力的本质是什么?

背景今天,我们来讨论一个问题:技术实力的本质究竟是什么?在工作中,你可能会遇到,为什么他的实力不如我,他却可以晋升?在面试中,你可能会遇到,我把系统性能优化了10倍,为什么面试官还是看不上我?为什么?到..

什么是VoidZero?Vue和Vite之父尤雨溪成立的技术公司

VoidZero是一家由Vue和Vite之父尤雨溪成立的技术公司,主要致力于解决JavaScript工具链的碎片化、依赖复杂以及性能瓶颈问题,通过提供一个统一、高性能的开发工具链来改善开发者的开发体验。优点分析统一性:VoidZer..

CLS 问题:超过 0.1(桌面设备) 是什么意思?

在网页设计和开发中,CLS(Cumulative Layout Shift)是指页面布局的累计偏移量。当一个用户与页面互动时,如果页面的某些元素突然改变位置或大小,导致整个布局发生偏移,就会产生布局偏移。这种偏移可能会影响用户..

什么是...rest?收集剩余参数

在 JavaScript 中,...rest 参数是一种特殊的语法,用于收集函数定义中所有剩余的参数,并将它们作为一个数组传递给函数。换句话说,它可以将不定数量的参数打包成一个数组。语法:function myFunction(...args) {//..

什么是.NET云原生应用程序?

.NET云原生应用程序是基于.NET技术栈构建的,专为云环境设计、部署和运行的应用程序。.NET云原生应用程序不仅充分利用云计算的优势,如弹性、可伸缩性和高可用性,还能通过现代云原生技术如容器化、微服务架构和自动..

发表回复

返回顶部