周爱民老师谈: 面向对象和结构化程序设计,javascript面向对象

661 阅读16分钟

面向对象

一个函数如果能够产生一个存活于这个函数之外的实例,那么这个实例就叫对象(Object),这个函数就叫

原书中是叫说是过程,不是用的函数,用函数也没关系。也可以理解成:

一个过程如果产生了一个存活于这个过程体之外的实例,那这个实例就叫做对象,这个过程就叫做类。

上面是最最最早期的OOP之父定义的面向对象,在这个时候没有我们现在说的封装,继承, 多态,为什么呢? 弄清楚这个问题,还是需要分清楚对象 和 对象系统的区别

对象系统

对象就是一个实例,说白了就是一个数据结构,和我们说的结构体或者说记录没有本质上的区别,做了一层封装,封装了一堆属性,一堆函数(行为),如果在结构体的层面理解的话就只有 属性和函数两种。如果在JavaScript上来理解,就只有属性一种,它封装了一堆属性。对象就是属性包,这是ECMAScript中的定义。

对象系统是什么 对象系统是一堆对象的集合。

那这一堆对象为什么要结合在一起,你是一个系统你总该有个系统原则吧?所以继承其实本身是系统原则。

继承: 说的是我们这个系统中的所有对象原本是有继承关系的,所以我们才是在一个系统中。

所以我们今天讲到的面向对象设计,本质来讲不是面向那一个对象的来做设计,而是指基于对象来设计对象系统的这样的一个设计

面向对象设计: 是指基于对象来设计对象系统的设计

继承

对象系统的第一个原则就是继承: 因为继承是对象最容易实现而且最容易被系统化的一部分。

有些东西就不再你的继承系统里面,对不起,我就是一个独立的对象,没问题,那你这样的独立对象就做设计好了可以给一个关系(这个对象为什么会放到这个系统里面,必须得给个理由,没有的话 就将扔到对象系统外边去。

多态:

一个对象同时对外表现一个A,又在另一个系统里面表现一个B。他可以表示出说他是一个老虎,他同时是一只猫科动物,又是个动物,又是个生物,他是表示在一个继承树上的多态。 另外一种多态就是他根本不在继承树上,经常说的话,他看起来像个鸭子,他就是个鸭子就是这个意思,看起来像是这个那就是这个, 一个东西看起来像多个东西,那也是多态。这种多态使用什么方式实现的 的呢?,在OOP程序设计中,使用interface来补充实现。同一个对象具有多个interface那他就有多个状态。所以到了这个对象类的或者继承树上的多态特性的时候是通过我们传统多态来实现的。 在继承树之外非继承体系中的多态是通过interface来实现的。有了这两个工具一个是继承树一个是叫interface,有了这两个工具你可以把所有所有的对象全部堆到这个某个系统里面,只要你认为 它与这个系统有关系。我定义一个特殊的interface可以把一些我原本没有涉及过得没有继承自 我的系统中的东西 也拿过来作为我的系统中的一部分,于是这个面向对象的系统规定做好了。

封装:

其实是对象的最基本特征,反倒比继承和多态这两项特性更为重要。封装的含义我把那些不想让用户看见的东西放在我的系统内,我想让用户可看见的东西把他放在这个界面上,所以才有了内部属性 和外部属性或者public属性这样的特性。而JavaScript本身非常特别它只有public最早早期的时候就一直只有公开属性,他所有属性都以公开方式存在的包括现在的符号属性原则上讲你也可以把 符号属性列举出来,也是公开的。只不过你看不到他的名字而已。,所以javascript本身来讲,就是全公开的,他没有我们所说的这种隐蔽这个级别的封装。但是他把一堆属性封装 到一起,打包成一个对象跟别的对象之间还是可以隔离的,js只做到了信息的隔离,而没有做到信息的隐蔽。这是jsOOP的对象的特点

OOP三大特性整体看

对象是不需要刚才所说的多态继承,这两个特性是对象系统所需要的,所以继承性和多态性不是对象的必然特性,有且仅有封装性对象的必然特性,而如果封装特性不出现在继承树上,就单个对象的封装特性这个事情也是可以不需要的,你可以不 public,那么这个对象就和其他对象信息隐蔽起来。所以就带来了我们在TC39里面,出现了一个非常非常有趣的局面,绝大多数的TC39成员都不喜欢对象的封装(信息的隐蔽)。私有属性这样的东西看都不看,觉得没啥意义。 因为TC39成员都偏向于做函数式设计,通常面向对象和函数式他们是对立的,因为他们对信息的隐蔽所采用的方法不同,所以他们基本上原始设计的出发点就不同。如果在一个更函数式的团队里面对面向对象的提案是不太会支持的 这是一个必然的结果。面相对象设计的好处就是大型系统的组织,我非常容易把一大堆所需要的编程的对象,按照这个继承的方式把他系统设计出来。在非继承系统中我无法去理解这个继承特性中我用interface把他绝大多数都关联 起来并且满足我们所有GOF的设计模式,几十种设计模式中的绝大多数。GOF中的设计模式全都是用interface来表现。在大型程序设计里面面向对象有非常大的意义就在于他非常容易来组织对象系统。而不是这个对象本身好不好。

javascript的对象系统其实不怎么好,因为只有public,站在系统架构的层面来讲,这个就有点显得过于简陋了,

javascript面向对象

javascript早期发布只有封装特性(将一堆属性进行封装),因为js是纯动态语言,多态是js的自然特性,天然就是多态,反正在都是在运行时决定,你愿意怎么改变就怎么改变,有点多态过头,没有约束变得。

早期1.0是没有继承,在1.1以后有了原型继承,到了1.3就是ES3和第五版都是一样的,到了ES6以后才有了类继承。面向对象设计中一共有三种继承,原型继承和类继承。第三种是元类继承在Ruby里面,在一些高级语言中也有Mate Class。用的人很少

原型继承

任何的父类上面改变了一下属性,子类里面全部都有啦。类继承过来的完全接受不了。

继承: 应该很严格的设计每个类的继承层次,在哪个地方出现某个属性或者某个方法,应该是非常非常明确的,但是在JavaScript中一切都变了,随便在Global(ObjectType)上面写一个属性, 然后所有属性都是那个样子,这个使用的不明确让人很迷惑。

原型继承特点非常明显,而且非常有它的优势,他带来了除了我们讲到过得这个系统的结构经验,1.占用内存少2.构建方便 3.系统组织灵活方便; 之外,还有一个重要特性是,直接返璞归真的定义了对象本身到底是什么。接下来看。。。。

如果我们今天再来看这个原型继承,所谓的属性就是原型链上的动态访问,我知道一个属性是什么其实是我自己查找自己和查找自己父类所有原型的属性的表的一个过程。这样的定义,和动态语言和函数式是天生绑定的, 属性的访问过程等于函数的调用过程,这个需要很清楚的明白,我一个属性的访问过程,是一个过程函数去一个动态查找的过程,属性的访问过程是动态实现的,而非是静态实现的,那我查找这个属性这里没有我到他的原型里面去找。再到他的父类原型中去找,这个过程是动态的。 我在父类原型中去定义一个属性的话,会动态的影响到我的任何的子类这个过程也是动态的。所以原型是最早的完整实现JavaScript混合语言特性的那个特性。我通过了这个叫动态语言特性和函数式语言特性来体现了面向对象的特性。或者来实现了面向对象特性 。你要是理解了这个实现过程,你发现原型继承这里是一个非常 非常精彩的地方。你看代价又很小,又混合多种语言范式实践,然后使用起来及其轻简方便。有什么不好呢?除了跟你之前学的语言涨的不太像以外。。。。

前端继承用的很少,面向对象思想也不多,这就是为什么TC39为什么比较倾向于函数式编程,前端UI都是通过组合来完成交互。

在前端和应用层面来讲,大体上一次两次就不得了了,在这个层次上面原型继承是不会带来很大困扰的,因为访问路径变得很短,属性产生的干扰很少很少,你也不会再多个层次上面出现多个相同属性。所以如果你实现大型的面向系统框架或者类库的时候。其实原型继承就不够用了,原型就会带来极大的困扰,这时候需要非常深的继承层次 ,关于类的设计。在这种情况下原先我们是面向对象里面的那种继承性和继承性里面带来的封装特性(继承树上的私有 保护 共有的封装)包括保护属性啊,他才有意义,否则你去做哪些事情是没有用的。

所以我们从类继承体系来看,原型继承的时候,原型继承太过简陋了。而且原型继承绝大多数的这种破坏性(父类添加属性子类动态拥有。子类去查找属性也是在原型链上动态获取的)都是 不能忍受的。因为类继承主要就是应对这种大型的深层次的类设计的时候。大型 类系统考虑的是类系统的稳定性,这就意味着类库的稳定,库稳定意味着在大型架构中的基础组件稳定,他不容易改,有了这样的强需求,才会出现我们刚才说的类继承的那些好处。所以那些开发类库和大型系统来说。 原型继承根本无法组织一个大型的类系统,原型继承就太小儿科了。

结构化程序设计

结构化程序设计

信息隐蔽原则带来了操作系统的变革,带来了程序设计语言的变革,带来了非常多的东西。也是结构化程序设计的基础理论和面向对象的基础理论。

信息隐蔽原则简单说就是: 一个数据实体,他应该是尽可能向外暴露他的数据和信息呢?还是尽可能不暴露他的内部信息和数据。应该是把对外界暴露的信息尽量的越少越好,你内部实现原则,尽量不要告诉别人,你只给别人可用的接口,有了这个基础理论之后结构化程序设计才说的通。

对外只暴露统一的结构就行了,结构内部如何实现的不需要让别人知道,这个是结构化程序的一部分。就带来了C语言的结构体,就带来了结构体转化而来的面向对象。

面向对象和结构体主要差异是面向对象是通过方法对外暴露信息(这个需要自己去研究还不是很理解),还有面向对象又部分东西是隐蔽的(保护或者私有)。

什么是结构化程序设计 第一: 数据的结构化 我们要把数据从字节双字节然后呢包括长整型等等还有在复杂的数组,结构体这些东西按照数据结构的方式定出来,大家都在相同的数据结构去操作实现。比如说: 双字节,长字节,长整型等等他其实 与这个硬件系统的实现是有关系的。它涉及到了cpu和这个存储间的带宽问题。处理好这些问题需要一个通用普世共识的规定,这样再能更好将结构定义是下沉这个硬件设计上期我们这个程序设计的一个纽带作用作为统一的解决方案

第二:逻辑的结构化 DJ特斯拉证明: 在它所知道的范围里,有且仅有三种就是顺序,分支和循环。只有论证了这一条你才能够把所有的逻辑减少到一个可控范围内。就是一个进口一个出口,这样也定义了函数式语言和过程式语言。

第三: 层次结构设计(面向对象) 把多个结构体,按照继承方式层次叠起来,说就是他的父级就是他的子集这样划分,这就是层次结构设计的早期,有了层次结构设计,才有了我们所说的分层和这个特性隐蔽的那个带多层之间的关系。

在这里一块只是起了一个头,他带来了一个更大领域的设计结构化问题,叫做层次的组织结构。java推荐一个class里面只写一个类,且占用一个文件,这就带来了文件的额暴增,这个由程序的组织结构控制做出思考。

你怎么把程序组织起来,从你的命名空间来组织也好,从类和类库或者单元这个维度上来组织也好,从哪怕代码片段 is else这样的分块来组织也好,这都是组织原则。

请问如何让程序有效组织起来,即容易阅读又容易编译然后还不容易出错,请问这有没有一个科学的解释呢。面向对象就是个很好的解释。

总结

  1. 面向对象万物皆对象,加法类(有点大)但是可以这么理解,能够更好的组织大型应用系统,写这种代码风格 要懂得结构化程序设计原则,抽象,封装,多态,继承。 函数式编程万物皆函数 更好的无副作用处理数据,从A到B。能够将数学问题抽象成程序函数(具有这样场景的也适用于函数式编程)。需要知道操作数可以是什么,函数可保存值,函数对外无副作用。

javascript是一个混合型语言,融合了很多编程范式,如果到一定阶段不分类学习都是在记知识,不会很好的灵活运用,以为没有目的性,没有架构风格支撑,是吧

  1. 很多语言就是对编程范式的期望和推演和实践。面向对象思想,能够很好的将万物抽象一个结构体,面向对象思想能够很好的组织这些结构体,这个思想能够解决问题,需要计算机解决问题。那就通过编码,编码要人写,那就定义人看懂语法语义。可以通过编译,计算机也能看懂,还能够实现思想语言就可以诞生了、

  2. 所以学语言 要确定语言范式和语言所要实现的思想,这样来作为一个支撑。再去学他的语义语法,再用这些语义语法去实现这门语言较容易实现的编程范式和思想,用来解决计算机可以解决的实际问题

  3. 所以我们不要太着急去学习另外一门语言,可以先了解基础程序设计有哪些通识思想,当需要一门语言去解决实际问题的时候再去深入了解和学习,基础数据结构有几个?类型系统怎么设计?用哪种方式实现继承?作用域怎么设计?垃圾回收可以怎么么做?带一堆思考和分类去学习一门语言,不要硬着头皮去学一门语言的api

  4. 像javascript这样的语言,混合型,不分类 就不好学了

  5. 我们计算机语言发展几十年都在结构化程序设计打转,在结构化程序设计之是空白的这些怎么思考很重要,这意味着我们程序解决不了所有的问题,至少有一半的问题你解决不了

参考连接: 周爱民老师开课吧采访