关于面向对象认知

822 阅读17分钟

"面向对象" 本质是什么

  • 对象是什么,面向对象本质是一个什么样的思想
  • 面向对象是什么,那这种设计思想的起源是什么,当时是为了解决那一类问题。
  • 什么是面向对象编程设计(OOP),是将面向对象思想用于编程形成一个编程的设计思想,架构风格么
  • 什么是面向对象语言(OOL),为什么说具有封装,多态, 继承特性的语言就是OOL语言呢?封装 多态 继承是面向语言独有的么,还是他们也是一种设计模式或者说是一种设计思想。
  • 为什么面向对象编程的架构风格和面向对象语言如此盛行,获得市场认可。
  • 是不是面向对象编程设计(OOP)无所不能么,还是一个复杂的系统会即有(FP)函数式编程又有(OOP)面向对象编程两种编程设计呢
  • 附加:javaScript 是一个"基于对象" 还是一个面向对象语言
    面向对象

1. 源头

  • 面向对象属于一个计算机科学范畴的设计思想,与传统的结构化设计方法属于两个设计思想或者架构风格
  • 传统的结构化设计方法的基本点是面向过程,系统被分解成若干个过程。而面向对象的方法是采用构造模型的观点,在系统的开发过程中,各个步骤的共同的目标是建造一个问题域的模型。在面向对象的设计中,初始元素是对象,然后将具有共同特征的对象归纳成类,组织类之间的等级关系,构造类库。在应用时,在类库中选择相应的类。
  • 面向对象编程找到这个设计思想的定位应用
  • 面向对象语言是将思想用编程语言实现

关于OO研究成果有很多但是离我们关系不大,程序员不需要针线活, 我们就从OOP进行面向对象追溯, 面向对象的程序设计的雏形,早在1960年Simula语言中即可发现,当时的程序设计领域正面临着一种危机: 在软硬件环境逐渐复杂的情况下,软件如何的带的良好的维护?而面向对象程序设计在某种程度上通过强调可重复性(可复用)解决了这一问题 (OAOO一个规则,实现一次(One rule, one place)是面向对象编程中的基本原则)

到了20世纪70年代的Smalltalk语言在面向对象方面堪称经典----以至于30年后的今天依然将这个语言视为面向对象语言的基础,作者Alan Kay也被称为 OO语言之父。 smalltalk发明大佬们,在谈到OOP时都是这样说的

I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages (so messaging came at the very beginning -- it took a while to see how to do messaging in a programming language efficiently enough to be useful)....OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP.

下面是大佬翻译的

OOP应该体现一种网状结构,这个结构上的每个节点“Object”只能通过“消息”和其他节点通讯。每个节点会有内部隐藏的状态,状态不可以被直接修改,而应该通过消息传递的方式来间接的修改。

群里面解释(雅仔-北京)

像大神说的那样,对象就像生物细胞那样,与外界只通过信息进行交换,所有内部状态的进程和变更都在内部进行,并且处于保护状态,不受外部影响。这样的“细胞”可以组合成很复杂的系统

划重点

面对在软硬件环境逐渐复杂的情况下,软件如何的带的良好的维护的危机.

2. 为了什么

因为软硬件开发不断的发展,软件需求量越大自然在后面出现的庞大复杂系统。OOP则能够支撑庞大复杂的系统!!!

上面说了一个像生物细胞(Obejct)一样,可以构成一个复杂的系统。 我通过知乎还有一位大神举了个例子,用开公司举一个例子:

小公司不繁杂,使用FP思想,有上帝视角可维护

如果公司特别小,就几个人,大家总是在一起干活,工作可以通过“上帝视角”完全搞清楚每一个细节,于是可以指定非常清晰的,明确的流程来完成这个任务。这个思想就接近面向过程编程

大公司,没那么容易可控,使用函数编程难以维护

而如果公司人数变多,达到几百上千,这种“上帝视角”是完全不可行的。在这样复杂的公司里,没有一个人能搞清楚一个工作的所有细节。为此,公司要分很多个部门, 每个部门相对的独立,有自己的章程,办事方法和规则等。独立性就意味着“隐藏内部状态”。

上一层次商业供应链,更是繁杂。

更高一层,公司之间也存在大量的协作关系。一个汽车供应链可能包括几千个企业,组成了一个商业网络。 通过这种松散的协作关系维系的系统可以无限扩展下去,形成庞大的,复杂的系统。这就是OOP想表达的思想

上面有面向对象编程设计实践者的表达,也有大神的解释,还有一些较为合适的例子,面向对象程序设计思想的使用能够提高软件的重用性,灵活性,和扩展性 可维护性。 当然这种编程思想也会有很好的传承。

1. 对象是什么

对象是什么

  • 在计算机科学中,对象(英语:object),台湾译作物件,是一个存储器地址,其中拥有值,这个地址可能有标识符指向此处。对象可以是一个变量,一个数据结构,或是一个函数。是面向对象(Object Oriented)中的术语,既表示客观世界问题空间(Namespace)中的某个具体的事物,又表示软件系统解空间中的基本元素。
  • 因为翻译的原因,中文语境下我们很难理解“对象”的真正含义。事实上,Object(对象)在英文中,是一切事物的总称,这和面向对象编程的抽象思维有互通之处。
  • 在软件系统中,对象具有唯一的标识符,对象包括属性(Properties)和方法(Methods),属性就是需要记忆的信息,方法就是对象能够提供的服务。在面向对象(Object Oriented)的软件中,对象(Object)是某一个类(Class)的实例(Instance)。
  • 在ECMAScript 2019规范中,An object is a collection of properties and has a single prototype object. The prototype may be the null value.(一个对象是一组属性,还拥有单一的原型对象,而且原型对象可以为null)。

但不论如何,我们应该认识到,对象并不是计算机领域凭空造出来的概念,它是顺着人类思维模式产生的一种抽象(于是面向对象编程也被认为是:更接近人类思维模式的一种编程范式)。

人类模式下: 对象这一概念在人类的幼儿期形成,这远远早于我们编程逻辑中常用的值、过程等概念。在幼年期,我们总是先认识到某一个苹果能吃(这里的某一个苹果就是一个对象),继而认识到所有的苹果都可以吃(这里的所有苹果,就是一个类),再到后来我们才能意识到三个苹果和三个梨之间的联系,进而产生数字“3”(值)的概念

在《面向对象分析与设计》这本书中,Grady Booch 替我们做了总结,他认为,从人类的认知角度来说,对象应该是下列事物之一:

  1. 一个触摸或者可以看见的东西;
  2. 人的智力可以理解的东西
  3. 可以知道思考或行动的东西

有了对象的自然定义后,我们就可以描述编程语言中的对象了。在不同的编程语言中,设计者也利用各种不同的语言特性来抽象描述对象,最为成功的流派是使用“类”的方式来描述对象,这诞生了诸如 C++、Java 等流行的编程语言。

计算机科学的对象本质具有特征是参考 Grandy Booch《面向对象分析与设计》):

  • 对象具有唯一标识性:即使完全相同的两个对象,也并非同一个对象。
  • 对象有状态:对象具有状态,同一对象可能处于不同状态之下。
  • 对象具有行为:即对象的状态,可能因为它的行为产生变迁。

所以,于是面向对象编程也被认为是:更接近人类思维模式的一种编程范式。

OOP(面向对象编程) 和 OOL(面向对象语言)

OO语言 !== OOP设计思想

比smalltalk更早的OOP语言Simula语言是为模拟环境而设计的(据说,他们是为了模拟船只而设计的这种语言,并且对不同船只间属性的相互影响感兴趣。他们将不同的船只归纳为不同的类,而每一个对象,基于它的类,可以定义它自己的属性和行为。) 模拟这个场景非常适合体现OOP思想。将真实世界的对象映射到抽象的对象,这叫做“模拟”。这个语言不仅引入了“类”概念,还有用实例这一个思想----这是这些思想的最早应用

Simula有了面向对象的主要思想,但Smalltalk中的对象是完全动态的----他可以被创建,修改并销毁,这与Simula中的静态对象有所区别,Simula类不具有扩展。用类声明出实例之后就是规定的实例,汽车类 声明出汽车实例,鸡就是鸡,鸭就是鸭。 但是smalltalk引入了继承性的思想,它因此一举超越了不可创建实例的实例的设计模型和不具备继承性的Simula。

同理封装,多态等等都是随着面向对象语言不断增多不断优化,不断引入的优秀的编程思想。

所以,并不是说OOP程序一定要用OOP语言来写,OOP是一种设计思想,是一种程序编程典范,抽象方针,而非仅仅是编码方式。更不能说OOP=封装 + 继承 + 多态。 封装, 继承, 多态都是可以脱离OOP独立的思想。面向对象编程设计思想本来就很容易理解,当时我学java的时候听到这些东西我是懵逼的听上去很牛逼但是使用起来经常和现实冲突以至于落不了地。 java c++等这些语言就以OOP为架构风格,参考smalltalk(继承)和Simula(类和实例)创建一门语言,解决当时需要当时需要解决的编程问题,像 封装,继承, 多态这些都是优秀的编程思想,而且可以借助面向对象编程语言做更好的诠释。 属于一门语言的一些软规则,很好的编程思想。告诉语言使用者使用这些特性编写出来的程序更好维护。所以说使用面向对象语言编写出来的代码不一定在用OOP架构风格,这个是很多初学者很多误解。

我们在以为我们在OOP时,其实很多时候都是在处理编码的细节工作,而非OOP提倡的“独立”,“通讯”。以“class”为例,实际上我们对它的用法有:

表达一个类型(和父子类关系),以对应真实世界的概念,一个类型可以起到一个“模版”的作用。这个类型形成的对象会严格维护内部的状态(或者叫不变量) 表达一个Object(即单例),比如XXXService这种“Bean” 表达一个名字空间,这样就可以把一组相关的代码写到一起而不是散播的到处都是,其实这是一个“module” 表达一个数据结构,比如DTO 这种因为代码复用,硬造出来的,无法与现实概念对应,但又不得不存在的类 提供便利,让foo(a)这种代码可以写成a.foo()形式

其中前两种和OOP的设计思想有关,而其他都是编写具体代码的工具,有的是为了代码得到更好的组织,有的就是为了方便。

所以说OOP是一种优秀的设计思想,架构风格。他能更好开发出易维护的复杂应用。OO语言 提供了很多的语法糖,供给语言使用者使用,从而希望使用者编写出符合OOP设计思想的代码,达到易于维护的目的。

OOP语言不能代替人类做软件设计,既然做不了设计,就做一些OOP相关思想的语法糖出来,供想编写OOP程序的人使用,所以一定要明确这一点很重要,OOP设计思想在前,OOP编码在后,程序不会自动变成OOP,也不一定能得到OOP的各种好处。

OOP设计思想的优秀案例:

  • OOP最典型的例子是互联网(一个网状结构网络,每个节点(隐藏属性,电脑,手机,路由)通过信息传输通讯)。
  • 还有个例子是linux内核(多个相对独立的组件(进程调动,内存管理器,文件系统)之间相互合作。

继承 和 多态,封装是OO语言的特征,他不是面向对象特征。这些特征也不是OO语言独有的,是OO语言也刚好具备这些优秀的特征。

“封装”,是想把一段逻辑/概念抽象出来做到“相对独立”

这并不是OOP发明的,而是长久以来一直被广泛采用的方法。比如电视机就是个“封装”的好例子,几个简单的操作按钮(接口)暴露出来供使用者操作,复杂的内部电路和元器件在机器里面隐藏。再比如,Linux的文件系统接口也是非常好的“封装”的例子,它提供了open,close,read,write和seek这几个简单的接口,却封装了大量的磁盘驱动,文件系统,buffer和cache,进程的阻塞和唤醒等复杂的细节。然而它是用函数做的“封装”。好的封装设计意味着简洁的接口和复杂的被隐藏的内部细节。这并非是一个private关键字就可以表达的。一个典型的反面的例子是从数据库里读取出来的数据,几乎所有的字段都是要被处理和使用的,还有新的字段可能在处理过程中被添加进来。这时用ORM搞出一个个实体class,弄一堆private成员再加一堆getter和setter是非常愚蠢的做法。这里的数据并非是具有相对独立性的,可以进行通讯的“Object“,而仅仅是“Data Structure”。因此我非常喜欢有些语言提供“data object”的支持。

再说说“继承”,是希望通过类型的 is-a 关系来实现代码的复用。

绝大部分OOP语言会把is-a和代码复用这两件事情合作一件事。但是我们经常会发现这二者之间并不一定总能对上。有时我们觉得A is a B,但是A并不想要B的任何代码,仅仅想表达is-a关系而已;而有时,仅仅是想把A的一段代码给B用,但是A和B之间并没有什么语义关系。这个分歧会导致严重的设计问题。比如,做类的设计时往往会希望每个类能与现实当中的实体/概念对应上;但如果从代码复用角度出发设计类,就可能会得到很多现实并不存在,但不得不存在的类。一般这种类都会有奇怪的名字和非常玄幻的意思。如果开发者换了个人,可能很难把握原来设计的微妙的思路,但又不得不改,再稳妥保守一点就绕开重新设计,造成玄幻的类越来越多…… 继承造成的问题相当多。现在人们谈论“继承”,一般都会说“Composite Over Inheritance“。

多态和OOP也不是必然的关系。所谓多态,是指让一组Object表达同一概念,并展现不同的行为

入门级的OOP的书一般会这么举例子,比如有一个基类Animal,定义了run方法。然后其子类Cat,Dog,Cow等都可以override掉run,实现自己的逻辑,因为Cat,Dog,Cow等都是Animal。例子说得挺有道理。但现实的复杂性往往会要求实现一个不是Animal的子类也能“run”,比如汽车可以run,一个程序也可以“run”等。总之只要是run就可以,并不太在意其类型表达出的包含关系。这里想表达的意思是,如果想进行极致的“多态”,is-a与否就不那么重要了。在动态语言里,一般采用duck typing来实现这种“多态”——不关是什么东西,只要觉得他可以run,就给他写个叫“run”的函数即可;而对于静态语言,一般会设计一个“IRun”的接口,然后mixin到期望得到run能力的类上。简单来说,要实现多态可以不用继承、甚至不用class。

引用:知乎 大宽宽

大家还有一个疑惑,javascript是一门基于对象的语言还是OO语言。答案是 javaScript面向对象语言。

  • 根据上面的讲解,就应该很明了了。只不过是js是使用比较冷门的方式,不是大多数语言一样基于类,而是基于原型。
  • 在 JavaScript 中,对象的状态和行为其实都被抽象为了属性。如果你用过 Java,一定不要觉得奇怪,尽管设计思路有一定差别,但是二者都很好地表现了对象的基本特征:标识性、状态和行为。
  • JavaScript 提供了完全运行时的对象系统,这使得它可以模仿多数面向对象编程范式(下一节课我们会给你介绍 JavaScript 中两种面向对象编程的范式:基于类和基于原型),所以它也是正统的面向对象语言。

对于面向对象也告一段落了,希望大家在此,可以在关于面向对象方面的知识有所收获 参考: