关于面向对象的深入理解
引言
第一次接触面向对象是在大一下学期的 Java 程序语言设计这门课上,听了老师讲了各种名词:类、对象、接口、继承、多态、封装……。当时也是一知半解,只是知道有这些东西,用的时候也是依葫芦画瓢,并不能理解其真正的设计内涵。随着项目经验的增加,也逐步体会出些许程序设计的精妙所在,再加之读各类书籍有所心得,故准备写这一些列文章,分享交流。
如果把程序员比作剑客的话,那么有两件东西最为重要:宝剑与剑术。宝剑可以理解为各种实打实的硬技能,比如:Java、MySQL、SpringBoot……,这些东西是死的,知识点很明确,及时与别人有所差距,下点功夫总能赶得上;而剑术则是程序 设计 之道,比如:需求分析、架构设计,这些东西相对来回活泛了许多,因为在这个过程中你所面对的不再是热腾腾的机器,而是各种各样的客户、复杂的业务逻辑和想法各异的同事们。所以,码农与程序员的差别就在于 程序设计 方面。设计本身就是一门艺术,那些统领千万代码大军的 CTO 们,是运筹帷幄的将军也是才华横溢的艺术家啊!
从面向机器到面向对象
最开始的计算机程序编码就是 0101,完全要以机器的二进制思维去思考问题;后来有了汇编只是编码形式上有了变化,但是本质并没有改变,程序员们的关注点还是在于机器本身,程序员要对 CPU、寄存器等硬件了如指掌;当 C 语言、COBOL 等面向过程的语言横空出世后,这是一次本质上的革命,把程序员从二进制的世界中解救了出来,终于可以让他们“像人一样”去思考问题啦。
此时程序员可以专注于问题本身,理清解决问题的具体步骤,然后将之用程序语言讲给计算机就可以啦。对于单一、具体的问题无论其复杂与否,采用面向过程的语言都可以完全 hold 住。这就如同流水线作业一般,环环相扣,问题复杂了无非就是工序多一些而已,整体来看难度不大。但是随着计算机硬件的发展,人们希望用计算机处理的事务也越来越多,业务逻辑也越来越复杂,此时再用面向过程的语言去实现如此纷乱的业务逻辑就有些难以招架了。就如同好多条生产流水线掺杂在了一起,A 线依赖 B 线上的一道工序,B 线又依赖于 C 线的一道工序,而 C 线还和 A 线有着关联;这样杂乱的流水线普通主管看了肯定会头大。此时就需要面向对象的程序设计语言来大杀四方了。
面向对象这种设计思想更加适合对于现实世界的模拟,更加适合处理一个系统性问题。它不再是以具体的点为中心,而是着眼于整个面。用类抽象出一群事物的共性,用接口定义好不同事物之间的交流规则,用继承的方式让同一类的事物也可以千姿百态,用多态的方式让千姿百态的事物也可以整齐划一。每个程序员都是代码世界的上帝,以面向对象的思想去设计整个世界的架构,以面向过程的思想去研究世界的每一株土木,方可达到一花一世界,一叶一菩提的境界。
面向对象三剑客
类
物以类聚,人以群分。——《战国策·齐策三》
类是整个面向对象设计理论的基石,只有把类搞清楚搞明白了才能让整个理论大厦有坚实的地基。
首先抛开程序设计, 单从现实世界的角度来看:枪保卫祖国的人叫军人, 在工地上干活的人叫工人,在高级写字楼里面办公的人叫白领。对于每一类人,他们每个个体都不相同,但是它们都有相似的特征,所以我们把他们归为的一类人。面向对象最根本的思想就是对于现实世界的模拟,用程序语言来表述业务逻辑。所以程序中的类可以这样理解:类是一组相似事物的统称,是对他们共性的一种归纳。 类具有属性和方法,从军人的角度来看他们所属于的部队、所驻守的地区……,这是军人的属性;他们可射击、可擒拿……这是他们的方法。
实际上世间万物皆可归于一类,因为只要有相似点,就有依据把他们归于一类。那应该如何来设计类呢?请记住这样一句话:横看成岭侧成峰,远近高低各不同。角度决定看法,一定要结合具体需求,来考虑类的设计。
类有属性和方法,那么如何划分属性和方法呢?这里应该遵循极简原则。即属性原子化、方法单一化。属性细化到不能再分割,方法只能做一件事。这样能够最大化地解耦,满足系统的可拓展性与易修改性。
对象
对于类的认知清晰以后,对象就比较容易理解了。
对象就是类的一个具体实例。以军人为例,黄继光,邱少云就是军人这一类人当中一个具体的实例。
其实关于类和对象还可以深入探究一下,为什么有了类还会有对象?类和对象各自的作用是什么?这个时候我们还是要回归于现实世界。中国的人很多,那么党和政府如何来组织管理我们呢?首先不同的人有不同的职业,比如医生、教师、军人、工人等等。政府依据不同类别的职业制定出了不同的政策,比如军人买票优先、教师游览一些景点免费。其实依据不同职业有不同治理策略是一种归类更是一种抽象。
抽象的本质就是隔离干扰、总结共性、降低事物的复杂度,站在更高的角度去看待问题,从而更好地把控全局。所以类对于程序员来说是一种抽象的工具,让我们能够更好地全局把控整个项目,组织管理整个项目。党和国家领导人提出了全面建设小康社会,提出社会主义核心价值观等理论,有些普通民众看起来是空话套话,原因他们的视野只局限于自己的一亩三分地,看到的是具体的事务,而习大大则是心系整个国家,他说的话是立足于整个中国,对于亿万华夏儿女都有用的话。所以习大大是对中国国情高度总结归纳抽象后才能提出这样的理论!
对于程序员来说,类是抽象的,对象是具体的,拥有了类就拥有了上帝视角,来俯瞰整个系统;拥有了对象就拥有的亿万子民,来进行劳动,创造价值。
接口
“接口”这个词是被滥用、乱用最多的术语了。抛开其他的接口不谈,这里主要是说一下面向对象中的接口。如果只看教科书上的定义,你很容易明白接口具体是什么:
Java 接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。—— 百度百科
但是光看这些冷冰冰的定义,却很难让人明白,尤其是对新手来说,为什么要用到接口?接口本质作用是什么?为了了解这个,还是得再次回归到现实世界中。想想有身边哪些接口:Micro usb、Type-C、HDMI……,为什么要用到这些接口?因为手机要连接电源,显示器要连接电脑……,从中我们进行一次抽象总结:接口是两种不同事物交互时的一个关卡,更是一组标准,定义了交互双方所应该遵循的规则。从程序的角度来看:这种规则就是一组相关的功能点的集合。举个栗子:目前有一个鼠标类,当某天我们这个鼠标类的对象需要与主机类的对象进行交互时,此时就应该去实现一个叫 USB 的接口,方可进行交互。
从设计的角度来看:接口着眼于某种具体的功能,是一种规范与约束;类着眼于某一类事物的共性,是一种总结与抽象。
三大核心特征
封装
这个概念从字面上很容易理解,就是把一堆东西封闭包装起来。现在我们来深入探究一下其设计内涵。在我看来封装的好处主要体现在两点:对外降低复杂度,对内保护数据。其实对于个人开发者来说,一个人写整个项目,第二点好处并不一定能体会的到。尤其是写属性的 get()与 set()方法之时,总是感觉多此一举。但是当面对系统性项目需要多人协同工作时,封装的意义就体现出来了。
第一:可以专人干专事,每个人管好自己的一亩三分地就 OK 了,对外暴露自己的实现的功能就行了,而用别人的模块时也只需要调用相应方法就行了,不用操心其具体实现。
第二:可以防止坏人做坏事,保护核心业务。比如小黑在一个银行系统项目组中做取款流程开发,当客户取过钱后小黑需要调用项目主管所提供的
public Boolean updateBalance(Float money,Int operation)
这一方法去修改余额,而不是能够直接访问用户余额这一关键属性。如果不进行封装,当某天主管怒斥了小黑一顿,小黑想搞点破坏,是不是可以把代码修改成用户取钱后,不进行减操作,而是直接访问用户余额这一属性给他加上一些钱哪。
继承
继承这一机制估计是大家用得最多的了,通过继承我们可以很方便地对父类进行修改与拓展。从设计者的角度来看继承是从抽象到具体的一个过程,架构阶段我们基于主要业务逻辑构建出代码的主框架,建好各种基类,这是一次归纳抽象的过程,细节设计阶段我们根据具体业务逻辑与基类,来构造出各种子类,这是一次展开具体化的过程。
多态
多态从字面上很容易理解,就是多种形态嘛。孙大圣七十二变,无论是变成石头、老头、大树其本质上都还是个孙悟空,紧箍咒都还对他管用。这样的好处就是可以屏蔽掉子类对象的差异,使得程序员可以写出通用性的代码,而无需针对不同的子类写不同的代码,让代码更易于拓展。
唐僧的紧箍咒对于孙悟空本身是一个方法,只要一念咒语猴子就头痛难忍,后来孙悟空变成了一个老头,唐僧不用专门去针对老头形态的孙悟空去研究一套紧箍咒,只用念原来的依然奏效。这样对于唐僧来说紧箍咒就具备的通用性与可拓展性,无论你猴子怎么变,唐僧都能以不变应万变!
总结
宝剑都是定型的,而剑术确是灵活的,虽然剑术也有一套规则,但在实战中是可以千变万化,形态万千。所谓好马配好鞍,互联网技术日臻成熟,作为开发者的我们有着数不清的宝剑,但拥有再厉害的宝剑也得配合一套自己了熟于心的剑术才能发挥其威力。程序员也如同剑客一般,剑客之道有三重:
手中有剑,心中有剑,人剑合一,第一境界;
手中无剑,心中有剑,杀人于无形,第二境界;
手中无剑,心中无剑,不武而屈人之兵,此乃第三境界!
不知道作为程序员的我们现在到哪中境界了哪?