面向对象(Object Oriented,OO)是软件开发方法。面向对象的概念和应用已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。
面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。
面向对象的三大特性是"封装、"多态"、"继承"
六大原则是"单一职责原则"、"开放封闭原则"、"里氏替换原则"、"依赖倒置原则"、"接口分离原则"、"迪米特原则"
三大特性:封装,继承,多态
封装
封装,就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。继承
继承,指可以让某个类型的对象获得另一个类型的对象的属性的方法。它支持按级分类的概念。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过 “继承”(Inheritance)和“组合”(Composition)来实现。
继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用 基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力。多态
多态,是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们那些不同的操作可以通过相同的方式予以调用。
六大基本原则:SPR, OCP, LSP, DIP, ISP,LOD
单一职责原则 SRP (Single Responsibility Principle)
初学者在编程的时候可能一开始会有这样的经历,使用一个类来实现很多的功能,新添加的甚至不相关的功能都放在一个类中来实现,煮成了一锅大杂烩,往往使得某个类包罗万象,无所不能。可能刚开始实现的功能比较简单,这样做不会引起很大的问题。但是随着之后项目复杂度的提升,各种不相关的代码耦合在一起,一旦有功能的更改或增删,修改的代码很可能导致其他功能不能正常运行,也就是违背了单一职责原则
单一职责原则是指一个类的功能要单一,不能包罗万象,就一个类而言,应该仅有一个引起它变化的原因。简单来说,一个类中应该是一组相关性很高的函数、数据的封装。单一职责的划分界限并不是总是那么清晰,很多时候都是需要靠个人经验来界定。当然,最大的问题就是对职责的定义,什么是类的职责,以及怎么划分类的职责。
在软件设计中,秉承着“高内聚,低耦合”的思想,让一个类仅负责一项职责,如果一个类有多于一项的职责,那么就代表这个类耦合性变高了,这些职责耦合在了一起,这是比较脆弱的设计。因为一旦某一项职责发生了改变,需要去更改代码,那么有可能会引起其他职责改变。所谓牵一发而动全身,这显然是我们所不愿意看到的,所以我们会把这个类分拆开来,由两个类来分别维护这两个职责,这样当一个职责发生改变,需要修改时,不会影响到另一个职责。
单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。
单一职责的好处
- 降低类的复杂度
- 提高可读性
- 提高可维护性
- 提高扩展性
开放封闭原则 OCP (Open-Close Principle)
开放封闭原则是所有面向对象原则的核心。
所谓开放封闭原则,软件实体(类、模块、函数等等)应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的当在不修改的前提下扩展。
开闭原则主要体现在两个方面:
- 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
- 对修改封闭,意味着类一旦设计完成,就可以独立其工作,而不要对类进行任何修改。
对于程序设计而言,如何设计才能面对需求的改变却可以保持相对的稳定,从而可以使得系统可以在第一个版本的基础上不断的推出新的版本呢?答案是在程序设计的时候使用开放封闭原则。但是在设计的时候,绝对对修改的关闭是不可能的,无论模块是多么的封闭,都存在一些无法对之封闭的变化,既然不可以完全的封闭,设计人员必须对他设计的模块应该对哪种变换的封闭做出选择,他必须猜测出最有可能发生变换的种类,然后构造抽象来隔离那些变化。
实现开放封闭的核心思想就是对抽象编程,而不对具体编程,因为抽象相对稳定。让类依赖于固定的抽象,所以对修改就是封闭的,而通过面向对象的继承和多态机制,可以实现对抽象体的继承,通过覆写其方法来改变故有行为,实现新的扩展方法,所以对于扩展就是开放的。
在具体类的设计和开发上,秉承这一原则:用抽象构建框架,用实现扩展细节。因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节,我们用从抽象派生的实现类来进行扩展,当软件需要发生变化时,我们只需要根据需求重新派生一个实现类来扩展就可以了。当然前提是我们的抽象要合理,要对需求的变更有前瞻性和预见性才行。
里式替换原则 LSP (the Liskov Substitution Principle LSP)
子类应当可以替换父类并出现在父类能够出现的任何地方。
在面向对象的语言中,继承是必不可少的,非常优秀的语言机制,它有如下优点:
- 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性
- 提高代码的重用性
- 子类可以形似父类,但是又异于父类。
- 提高代码的可扩展性,实现父类的方法就可以了。许多开源框架的扩展接口都是通过继承父类来完成。
- 提高产品或项目的开放性
但是所有的事物都有二面性,继承除了有上述优点,也有下面缺点:
- 继承是侵入性的,只要继承,就必须拥有父类的所有方法和属性
- 降低了代码的灵活性,子类必须拥有父类的属性和方法,让子类有了一些约束
- 增加了耦合性,当父类的常量,变量和方法被修改了,需要考虑子类的修改,这种修改可能带来非常糟糕的结果,要重构大量的代码。
里氏替换原则为良好的继承定义了一个规范,主要有下面四个方面:
- 子类必须完全实现父类的方法
- 子类可以有自己的个性
- 覆盖或实现父类的方法时输入参数可以被放大
- 覆盖或实现父类的方法时输出结果可以被缩小
依赖倒置原则 DIP (the Dependency Inversion Principle DIP)
- 高层模块不应该依赖低层模块,两者都应该依赖抽象
- 抽象不应该依赖细节
- 细节应该依赖抽象
每一个逻辑的实现都是由颗粒原子逻辑组成的,颗粒原子逻辑就是低层模块,而颗粒原子逻辑组成的模块就是高层模块。在java语言中,抽象就是接口或抽象类,两者都是不能直接被实例化的,细节就是实现类,实现接口或继承抽象类而产生的类就是细节,两者都可以直接被实例化。
也可以说高层模块,低层模块,细节都应该依赖抽象;
依赖倒置原则在java语言中,表现是:
- 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。
- 接口或抽象类不依赖实现类
- 实现类依赖接口或抽象类
对依赖倒置原则更加精简的理解就是“面向接口编程”
接口分离原则 ISP (the Interface Segregation Principle ISP)
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
在程序设计中,依赖几个专用的接口要比依赖一个综合的接口更灵活。接口是设计时对外部设定的“契约”,通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
与单一职责的区别
很多人会觉的接口隔离原则跟之前的单一职责原则很相似,其实不然。
其一,单一职责原则原注重的是职责;而接口隔离原则注重对接口依赖的隔离。
其二,单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节;而接口隔离原则主要约束接口,主要针对抽象,针对程序整体框架的构建。
采用接口隔离原则对接口进行约束时,要注意以下几点:
- 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
- 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
- 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
迪米特原则 LOD (Law of Demeter)
迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University的Ian Holland提出,即:
一个对象应该对其他对象有最少的了解
类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。于是就提出了迪米特法则。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。
迪米特原则的具体要求:只与直接的朋友通信
首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,当前对象本身(this)、成员变量、以参数形式传入当前对象方法中的对象、方法返回值中的类,当前对象创建的对象为直接的朋友。
迪米特法则可降低系统的耦合度,使类与类之间保持较低的耦合关系。我们知道,软件编程有一个总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。