佛牌网

您现在的位置是:首页 > 相术命理 > 相术

相术

面相对象和面相过程的区别(面向过程和面向对象有什么区别)

2023-04-06相术
效率最高,对机器最友好的自然是机器语言程序(按机器指令集直接编写机器可以识别的二进制代码序列)。对机器友好,对人却不是很友好,难写,难读,难维护。就如同象形文字

效率最高,对机器最友好的自然是机器语言程序(按机器指令集直接编写机器可以识别的二进制代码序列)。对机器友好,对人却不是很友好,难写,难读,难维护。就如同象形文字一样,虽然够形象,但不够抽象,不方便表达和交流。

所以,机器语言需要抽象,用一些英文词或字母组合缩写来表示机器指令,做成一个手册来描述机器或CPU,程序员按这些字母组合来写程序,然后替换一下,这样似乎可行,而查找替换正是计算机所擅长的,另写一个这样的查找替换的翻译程序即可,其实这就是汇编语言,翻译程序就是汇编器。

人的欲望是没有止境的,我们希望写程序更快捷,更易读,更易维护,更接近人类语言,那就来更高一层的抽象吧,将一些复用场景较高的代码块规范化为一个英文词,由汇编的一对一的关系转变为一对多的关系,一些操作直接用运算符来代替,流程跳转用goto代替,后来甚至发现,只用三种流程结构(顺序、选择、循环)和和任意数量的布尔型标志就能表示任何功能或算法,这就是结构化编程语言。结构化编程语言也叫面向过程编程语言,用数据描述问题,对函数描述解决问题的步骤,将关联性较高的数据和函数放到同一文件中,通过文件来区隔接口和模块。

当面对成百上千、成千上万的函数时?是不是有心乱如麻的感觉?一种常用的方法就是分类,自然又会是更高一层的抽象,使用更大一点颗粒,如果组装产品,组件越大,组件的接口越友好,组装更便捷(需要注意的是,有些产品是不适合组装的)。这种更高抽象,更大颗粒的编程思想就是面向对象(OOP,Object Oriented Programming)。

基本数据类型有值域和操作集(适用的操作符);

类类型的可以理解其值域是数据成员,操作集是其成员函数或重载的操作符。

在面向对象编程中,抽象的核心是数据,操作这些数据的成员函数是辅助,以数据为中心,由成员函数提供接口,以消息进行联系。相对而言,面向过程抽象的核心是处理数据的过程。

面向过程也只是一种抽象方式而已。

1 一种函数和数据的耦合的方法而已

面向对象编程的主要思想非常简单:尝试将一个功能强大的程序整体分解为功能同样强大的多个部分。这样就可以将一些数据和那些只在相关数据上使用的函数耦合起来。

注意,这仅涵盖封装的概念。也就是说,位于对象内部的数据和函数对于外部是不可见的。我们只能通过消息(通常通过 getter 和 setter 函数)与对象的内容进行交互。

什么叫封装?封装就是把一个复杂的内部逻辑,以一个相对简单的界面对外提供的过程。无非是把对状态的定义和操作状态的函数 (所谓"方法") 的定义写到一个花括号里的特殊编码风格,其方法多了一个this指针,其数据多了一个命名空间。

f(o); // 函数以需要处理的数据为参数,f 是默认的全局空间

o.f(); // 函数有内聚的数据成员,通过一个隐含的this指针来引用,同时有一个命名空间

继承性和多态性并没有包含在最初的设计想法中,但是对于现在的面向对象编程而言是必需的。继承基本上意味着开发者可以定义具有其父类所有属性的子类。直到 1976 年,即面向对象的程序设计的概念问世十年之后,继承性才被引入。

又过了十年,多态性才进入面向对象的编程。简单来讲,这意味着某种方法或对象可以用做其他方法或对象的模板。从某种意义上说,多态性是继承性的泛化,因为并不是原始方法或对象的所有属性都需要传输到新实体。相反,你还可以选择重写一些属性。

多态性的特殊之处在于,即使两个实体在源代码中互相依赖,被调用实体的工作方式也更像插件。这使得开发人员的工作变得轻松,因为他们不必担心运行时的依赖关系。

值得一提的是,继承性和多态性并不是面向对象编程所特有的。真正的区别在于封装数据及其包含的方法。在计算资源比今天稀缺得多的时代,这是一个天才的想法。

函数式编程的核心原则是数据只与功能函数进行松散的耦合。

2 一种代码约束方式而已

结构化编程,面向对象编程、函数式编程)这三个编程范式分别限制了goto语句、函数指针和赋值语句的使用。

结构化编程是对程序控制权的直接转移的限制。

面向对象编程是对程序控制权的间接转移的限制。

函数式编程是对程序中赋值操作的限制。

在没有OOP以前,C语言就可以实现将数据和函数封装为一个模块,隐藏私有数据的同时对外暴露公开的函数。同样的,在OOP以前,C语言也可以实现继承。当然OOP使得实现继承更方便了。

在没有OOP以前,通过函数指针一样可以实现多态。但是OOP使得多态的实现更加安全和方便。使用函数指针时需要遵循一系列人为的约定或惯用法,如果在这方面稍有疏漏就可能产生非常难以跟踪与消除的错误。而OOP提供的多态机制消除了这样的风险。

函数式编程的概念可以追溯到二十世纪三十年代Alonzo Church发明的Lambda-算子。但是函数式编程之所以最近又“火”了起来,是因为函数式编程比指令式编程需要多得多的内存和处理能力。这只有在存储器和计算能力非常廉价的今天才有可能实现。

函数式编程限制了我们对变量的使用。纯粹的函数式编程中,变量都是“不可变”的(immutable),实际上等于取消了“变量”。

竞争条件(race conditions)、死锁、并发更新,这些并发程序中常见的难题都是由于可变的变量引起的。函数式编程通过强制immutable,也就消除了这些问题。面向对象编程代码鼓励使用共享的可变状态,这一点多次证明是不安全的。

引用透明,没有重新赋值。

f(a) == f(b) when a == b.

这三个编程范式都对程序员提出了新的限制。每个范式都约束了某种编写代码的方式,没有一个编程范式是在增加新的能力。

也就是说,我们过去50年学到的东西主要是:什么不应该做。

应当说,面向对象和函数式不冲突。但还是很多人觉得用函数式就等于要丢下面向对象。

但只有小孩子才做选择,单一范式总是存在缺陷的。

面向对象编程的一个基本限制是它将所有东西都强制转换成名词。不是所有的东西都应该被建模为名词。面向对象强调的是一种搭积木的方式,由此衍生出设计模式。操作(功能)不应被建模为对象。只需要一个将两个数字相乘的函数,为何却要被迫创建一个乘法类?简单的使用一个乘法函数,让数据成为数据,让函数成为函数!

3 一种代码组织方式而已

最初的时候,代码都很短小精干,几十上百行而已,一行一行的语句写下去就OK了;

随着需求的增加,慢慢地,代码量开始加大,出现了成千上万行的代码。这时候,代码的组织管理就出现了问题。怎么办?引入函数。上万行代码被组织成了百十来个函数,是不是好管理多了?就像上万名士兵,按师团连排组织起来一样。

然而,软件工程的规模进一步扩大,导致一个项目出现了成千上万个函数!这时候怎么办?我们是不是又只能“分门别类”地把这些函数组织起来?于是出现了类,继而出现对象和面向对象……

一开始学习的时候,你不要去管什么“重用”“耦合”、“可扩展”、“封装变化”……等等这些东西,首先理解:不得已!

使用函数,使用类是我们不得已而为之的事情啊!

不能理解“不得已”,是不能理解“面向对象”的关键。

我们很多课程书籍,一介绍面向对象,就开始介绍面向对象的优势,吧啦吧啦,这其实是一个……一个什么呢?不能说这样做错了,但是,会增加入门者理解面向对象的难度。

更进一步,不能理解“不得已”,也会阻碍理解“设计模式”:

理解面向对象,一定要首先理解:面向对象解决的是“代码组织”的问题。不是代码实现。

4 GUI的最佳拍档

在历史上OOP最辉煌的时刻之一,应该是MFC。

虽然MFC现在看起来十分简陋和难用,但是这套复杂系统从出现到普及,到解决很多实际问题、提供了各种扩展方案。这个庞大而复杂的系统成功解决了windows这种基于GUI的软件的开发,完成了当时的历史使命。

OOP的普及和程序界面图形化的大潮是密不可分的,GUI恰恰是OOP最擅长的领域之一。甚至到了今天,在某些纯C语言编写的游戏引擎中,一旦到了UI部分,还是要在C语言里使用OO的写法和思路。在设计控件时使用继承真的是再合适不过了。

在游戏引擎中,都没有也不可能脱离OOP,或多或少。因为模拟世界中的各种物体,这件事也是OOP所擅长的。但是现在很多人在编写具体逻辑时,确实喜欢避免继承,这和纯粹的OOP思路不太一样。简单来说,引擎提供的组件本身是用到了继承的,但是进一步编写游戏逻辑时又要避免深一步的继承。

事实证明,OOP在某些问题领域确实比其他方法更出色。例如,OOP仍然是构建用户界面(窗口和按钮)的最自然的方式。但是,试图使面向对象适应关系数据库一直以来都简直是一场灾难。如果明明没有对象可呈现,非要搞个对象出来是不合适的。

写在最后

代码如何组织?如何实现模块化?如何降低复杂性?数据与处理数据的代码如何耦合?如何实现代码重用?如何实现状态隔离?这些都是编程范式的关注焦点,单一范式总是存在片面性,多范式才是可取的。

-End-

文章评论