OOP理念
面向过程让计算机有步骤地顺次做一件事情,是一种过程化地叙事思维。但是在大型软件开发过程中,发现用面向过程语言开发,软件维护、软件复用存在着巨大困难,代码开发变成了记流水账,久而久之就称为“面条”代码,模块之间互相耦合,流程互相穿插,往往牵一发而动全身。面向对象提出一种计算机世界里解决复杂软件工厂的方法论,拆解问题复杂度,从人类思维角度提出解决问题的步骤和方案。
比如“开门”这个动作,面向过程是“open(Door door)”,“door”是被作为操作对象的参数传入方法的,方法内定义开门的具体步骤实现。而在面向对象的世界里,首先定义一个对象“Door”,然后抽象出门的属性和相关操作,属性包括门的尺寸、颜色、开启方式(往外开还是往内开)、防盗功能等;门这个对象的操作必然包括open()和close()两个必备的行为。面向过程的结构相对松散,强调如何流程化地解决问题;面向对象地思维更加内聚,强调高内聚、低耦合,先抽象模型,定义共性行为再解决实际问题。
但是,编程语言仅是一个工具,就像练武之人地剑,武功高者草木皆剑,武功差者即使莫邪剑在身也依然平庸。所以,能否将工具的价值发挥得淋漓尽致,最终还是取决于开发工程师本身。优秀的开发工程师用面向过程的语言也能把程序写的非常内聚,可扩展性好,具备一定的复用性;而平庸程序员用面向对象语言一样能把程序写得松散随意、毫无抽象与建模、模块间耦合严重、维护性差。
传统意义上,面向对象有三大特性:封装
、继承
、多态
。本文(参考的《码出高效》一书)明确将“抽象
”作为面向对象得特性之一,支持面向对象“四大特性”得说法。抽象是程序员的核心素质之一,体现出程序员对业务的建模能力,以及对架构的宏观掌控力。虽然面向过程也需要进行一定的抽象能力,但是相对来说,面向对象思维,以对象模型为核心,丰富模型的内涵,扩展模型的外延,通过模型的行为组合去共同解决某一类问题,抽象能力显得尤为重要;封装是一种对象功能内聚的表现,使模块之间耦合度变低,更具有维护性;继承使子类能够继承父类,获得父类的部分属性和行为,使模块更有复用性;多态使模块在复用性基础上更加有扩展性,使运行期更有想象空间。
抽象是面向对象思想最基础的能力之一,正确而严谨的业务抽象和建模分析能力是后续的封装、继承、多态的基础,是软件大厦的基石。在面向对象的思维中,抽象分为归纳和演绎。前者是从具体到本质,从个性到共性,将一类对象的共同特征进行归一化的逻辑思维过程;后者则是从本质到具体,从共性到个性,逐步形象化的过程。在归纳的过程中,需要抽象出对象的属性和行为的共性,难度大于演绎。演绎是在已有问题解决方案的基础上,正确地找到合适的使用场景。演绎错误在使用集合时比较常见,比如针对查多改少的业务场景,使用链表是非常不合理的;底层框架技术选型时如果有错误,则有可能导致技术架构完全不适应业务的快速发展。
封装的主要任务是对属性、数据、部分内部敏感行为实现隐藏
。对属性的访问与修改必须通过定义的公共接口来进行,某些敏感方法或者外部不需要感知的复杂逻辑处理,一般也会进行封装。
继承是面向对象编程技术的基石,允许创建具有逻辑等级结构的类体系,形成一个继承树,让软件在业务多变的客观条件下,某些基础模块可以被直接复用、间接复用或增强复用,父类的能力通过这种方式赋予子类
。继承把枯燥的代码世界变得更有层次感,更有扩展性,为多态大侠语法基础。
人人都说继承是is-a关系,那么如何衡量当前的继承关系是否满足is-a关系呢?判断标准即是否符合里氏代换原则。任何父类能够出现的地方,子类都能够出现。
方法污染是指父类具备的行为,通过继承传给子类,子类并不具备执行次行为的能力。方法爆炸是指继承树不断扩大,底层类拥有的方法虽然都能够执行,但是由于方法众多,其中部分方法并非与当前类的功能定位相关,很容易在实际编程中产生选择困难症。提倡组合优先原则来扩展类的能力,即优先采用组合或聚合的类关系来复用其他类的能力,而不是继承。
多态是以上述的三个面向对象特性为基础,根据运行时的实际对象类型,同一个方法产生不同的运行结果,使同一个行为具有不同的表现形式。我们来明确两个容易混淆的概念:“override”和“overload”,“override”译成“覆写”,是子类实现接口,或者继承父类时,保持方法签名完全相同,实现不同的方法体,是垂直方法上行为的不同实现。“overload”译成“重载”,方法名称是相同的,但是参数类型或参数个数是不同的,是水平方法上行为的不同实现。多态是指在编译器层面无法确定最终调用的方法体,以覆写为基础来实现面向对象特性,在运行期间由JVM进行动态绑定,调用合适的覆写方法体来执行。