简介
设计模式的书籍并不少见,各有特色,那为何要另起炉灶?答曰:“为了更贴近实际理解,而不是应试。”
本系列采用java语言来表示,不同语言稍微有所不同,这里不再加以区分。
1. 单一职责原则
又称SRP
,定义:一个类,应该有且仅有一个引起它变化的原因,换而言之,一个类的函数应该有很高的相关性,其数据也是如此。
比方说,一个用于输出的类System.out
,就是由相关性很高的输出函数封装的。
注意,由于对类的职责 定义、是什么、怎么划分 每个人都有不同见解,这需要创作者自己定义。 最基本的是,但两个完全不一样的功能就不应该放在同一个类中。
2. 开闭原则
又称OCP
,定义:软件中的对象(类、模块、函数等) 应该
对继承(扩展)是开放的,对修改时封闭的。,具体操作起来,也就是在软件生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行维护修改时,**不应该修改原代码,而是通过扩展实现变化。**当然,这是理想化的模型
,为了避免代码过于臃肿和难以理解,仍然会有修改代码的情况。
如果说,某些代码修改后会引起其它问题,只有在遵循本原则下,才能确保原有软件模块的正确性,以及尽量少影响原有模块。
典型例子是,安卓View.java已经增加到恐怖的3万多行代码,越发臃肿,现在,Compose的出现也有部分目的是为了弥补它不足。
当然,在java中,给一个类添加新的功能或者之后为其有所修改,可以通过接口来实现。
3. 里氏替换原则
又称LSP
,它有两种不同程度的定义:
- 严格的说,C类型的对象,都有F类型的对象与之匹配,使得所有有T类型对象的程序在全替换时候,对程序无影响,那么可知:C是F的子类(
C
hild是F
ather的子类) - 简单地说,所有使用父类的地方,换成子类也没问题。
打个比方,Java的Set类,你可以使用HashSet,也可以是TreeSet,这就是遵循了里氏替换原则。
里氏替换原则核心是抽象,而抽象依赖于继承,在开闭原则中,我们就谈到了继承(扩展)的优缺点:
- 代码重用,减少创建新类成本,每个子类都有父类的方法、属性
- 与父类相似,但有区别
- 提高代码的可扩展性。
缺点:
- 继承会暴露父类某些不愿意透露给子类的方法,有可入侵性。
- 子类代码冗余,不够灵活摆脱父类方法和属性。
开闭原则和里氏替换原则是一对鸳鸯,在开发中是不离不弃的,同时他它们也共同强调了抽象这一特性。
4. 依赖倒置原则
又称DIP
,依赖倒置原则 是一种特定的解耦方式:使得高层次模块不依赖于低层次的模块的实现细节的目的,依赖被颠倒了。
并且有几个关键点:
- 高层次模块不应该依赖低层模块,两者都应该依赖其抽象。
- 抽象不应该依赖细节
- 细节应该依赖抽象
注意,在Java中,抽象就是 接口、抽象类,他们不能实例化;细节就是实现接口、实现类或者继承抽象类而产生的新类,他们可以实例化。高层模块
就是调用端,底层模块
就是具体实现类。
由于翻译的原因,笔者更偏向于更详细描述 依赖 的意思:
- 高层次模块不应该 依靠 低层模块 来实现目的,两者都应该依赖其
抽象
所指定的关系来实现。 - 抽象的关系要指明,不应该由细节来决定
- 细节应该由抽象的定义依赖关系规则来 丰富 其功能
总结:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或者抽象类产生的。
所以说,面向接口编程,抑或面向抽象(接口、抽象类)编程,是开闭原则、** 里氏替换原则**的精髓之一。
举个例子,请看以下代码,和使用本原则后的结果。
实现压缩程序
众所周知,压缩算法有很多种,我们拟做一程序实现一个应用,默认是Zip压缩:
上面省略了无关代码,假设这样写没有问题,那么现在,用户想使用7z来压缩文件,怎么办?
这时候,我们需要引入7z压缩算法,在构造函数 参数传入 选择的算法 并写好解析,然后再在输出做好决定:
这样写是非常复杂的,假如再来个rar压缩呢?是不是代码无意义的增加了?假如用户想自定义呢?是是很棘手?
所以我们可以简化一下:
这样,就简单多了,而且也十分灵活。
我们发现,上面几种方法,似乎抽象是让代码灵活的唯一方式。
5. 接口隔离原则
又称ISP
,一种定义是:客户端不应该依赖它不需要的接口 。另一个 定义是:类间的依赖关系应该建立在最小的接口上。
也就是说,ISP是将非常庞大、臃肿的接口拆分成更小的和更具体的接口,这样客户将会只需要知道他们感兴趣的方法。
接口隔离原则目的是使得系统解开耦合,从而容易重构、更改和重新部署,也可以在调用指定接口方法而不用暴露其他方法,避免方法泄露。
Bob大叔 (Robert C Martin )在21世纪初就将上述五个原则定义为SOLID原则,足以发现他们在程序的重要性,经过学习,我们就能概括其本质:抽象、单一职责、功能尽量分化
6. 迪米特原则
又称LOD,也叫作最小知识原则(Least Knowledge Principle),定义是:一个对象对其他对象仅有最小了解。,也就是说:“一个类对自己需要耦合的或调用的类知道的最少,类内部实现和调用者无关,调用者只要知道它需要的方法就可以了。”为什么这么做?因为,类和类之间关系越密切,当一个类发生改变时,另一个极有可能改变。
举例:给多个类的数组排序,调用Arrays.sort(),而不用关心其中的算法。