设计模式——六大原则

1. 单一职责原则(Single Responsibility Principle,SRP)

定义:就一个类而言,应该仅有一个引起它变化的原因

程序最终期望实现的是高内聚,低耦合,而内聚就体现在这里

就像一个团体一样,既然赋予了它姓名,那么参与者们就都有着相似的特征,并且内部的设施都是为此而服务的

拿足球俱乐部举个例子,其实可以把它当成是一个类,那么里面的人,可以看做是类的成员,比如:球员、教练、队医、司机

而各种设施也是围着球队工作,可以把它们当做是类的方法,比如:球场集体训练、健身房强化锻炼、更衣室冲澡这些都是为这个整体服务,与足球俱乐部这个集体紧密相关

因此,在代码层面上的表现就是:这个类有着相关性很高的函数(Method)以及数据(Field)

然而相关性这一点其实界限很模糊,因此这往往就依赖于开发者自身理解和工作经验了()

2. 开闭原则(Open Close Principle,OCP)

这是Java世界里最基础的设计原则

定义:软件中的对象(类、模块、函数等)应对于扩展开放,但是对于修改是封闭的

我认为,这可以理解成对现有的逻辑代码的一种保护,因为现有功能是稳定的,后期的修改应当尽量避免引入新的问题

当然,在某种程度上,这也显得较为理想化,实际的开发过程却难免会因产品快速迭代与变更需要修改原有内容

因此,在代码层面为避免引入更多的问题,可以尽量选择扩展,也方便他人对已有内容进行实现

3. 里式替换原则(Liskov Substitution Priciple,LSP)

定义:所有引用基类的地方必须能透明地使用其子类的对象

里氏替换原则主要就是依赖于面向对象语言三大特性中的继承与多态

简单点说,就是父类出现的地方统统可以使用其子类进行替代,并且能够正常工作

有点前人栽树,后人乘凉的意味

而里氏替换原则的核心则是抽象

仅仅需要对应的对象最低限度啊满足它的职责(接口或抽象类的定义)

优点:

  • 代码重用,减少创建类的成本,每个子类都拥有父类的方法和属性
  • 子类满足与父类的共性,同时又拥有自身的个性
  • 提高了代码的可扩展性

缺点:

  • 继承是侵入性的,只要继承就必须拥有父类的所有属性和方法
  • 子类代码可能冗余,灵活性下降,因为不可避免地拥有父类的属性和方法

里氏接口替换原则往往和开闭原则紧密相关,很好地支持外部自定义的操作

4. 依赖倒置原则(Dependence Inversion Principle,DIP)

定义:依赖倒置原则体现的是一种特定的解耦方式,使得高层模块不依赖于底层模块的实现细节

定义看上去很模糊,因此需要根据实际场景进行分解

首先,有以下关键点需要注意:

  • 高层模块不应依赖于低层模块,二者应该都依赖于抽象
  • 抽象不依赖细节
  • 细节应当依赖抽象

如果从Java语言的角度进行理解,抽象无非就是以接口或者抽象类的形式进行体现的,主要目的是明确职责

所谓抽象正是代表了一种形式上的东西,需要遵守

另一方面,细节体现的是接口的实现类或者是抽象类的实体子类,它们在遵守形式的基础上真正地去履行自己的职责

这样,后两条就很清晰了

image-20220702122051298

再举个例子,人类可以认为是一个抽象的概念,如果简单点进行定义,可以只有名字、年龄,能吃能睡,这里用了抽象类进行表示

而程序员,依赖人类的身份(对于People抽象类进行实现),首先履行了人类职责,拥有吃、睡的传统艺能(从父类继承而来),还会敲代码,并且还有账户(另外加的)

程序员对于吃和睡有实实在在的行动,而非只是标签:抢消费券点外卖吃,半夜回家倒头就睡(这便是对于细节的描述)

而对于第一点,首先要注意高层次模块和低层次模块的含义

当一个项目增长到一定的量级,往往会对于其功能进行拆分,以明确代码的职责,也方便业务的扩展和梳理逻辑,减少问题

通常会将具体业务代码,根据其实现的业务功能进行划分,比如说网络模块、即时通信模块、通用工具模块、线上课程模块等

越位于下层的模块其本身的独立程度越高,比如通用工具模块,它们作为功能实现端,给其他模块调用,就像平时功能不好实现时上github找包一样,只需要关注需要用到的方法,因为具体的功能下层已经实现了(工具已经有了,看你上层怎么用)

上层与下层的关系往往是相对的,因为你是用东西的人,你的地位高一些,但是更上层还会有人等待着你提供服务

因此,上层相对于低层次是调用方

现在二者都依赖于抽象的意思,如果用Java语言理解,那么应该就是二者通过接口或抽象类进行交互,而不是直接进行调用

image-20220702224416704

原先高层直接调用低层,这就不得不让上层每次都亲自出马(上层主动依赖)

而现在,上层给了标准,需要人办事,下级直接按标准对号入座,所谓关注结果(上层提供标准,下层实现标准)

实现面向接口编程

5. 接口隔离原则(Interface Segregation Principle,ISP)

定义:类间的依赖关系应该建立在最小的接口上

该原则也仅仅围绕抽象

也就是说,对于某一类操作进行调用,那么取能够适配更多的接口,作为参数

这样,其他实现该接口的类可以直接使用,遵循里氏替换原则,而无需关注内部的细节

接口隔离的目的是使系统解开耦合,从而容易重构、更改和重新部署

在Android中,Context经常会被用到,而Activity和Fragment等都是对于Context的某种实现,因此在设计方法的时候,如果需要好的兼容性,并且Activity和Fragment的作用可以用Context替代的话,那么就把Context类型作为参数,因为它是更高层次的抽象,能够应用更多的场景,而无需为每个类型单独考虑

就像你去公司或健身房一样,门禁并不关注你是谁,男的还是女的,只关注你有没有卡或者录入人脸,因为对于门禁来讲,谁来都一样,没有必要量身定制

6. 迪米特原则(Law of Demeter,LOD)

定义:一个对象应该对其他对象有最少的了解

其实这里主要体现出的还是一种抽象与解耦思想

类与类之间不可避免地会有引用,但是只保留必要的引用

这就像手机里下载App一样,只保留我们平时需要用的,其他不怎么用的可以删掉,这样才能保持系统整洁干净、易于管理

并且,让专业的人做专业的事,我们只是应用的用户,功能的内部实现不关注,这便是一层抽象,回到了之前的依赖倒置原则

也可以看得出,6大原则间其实也是紧密关联的