Note/六大设计模式.md
2023-08-31 11:30:31 +08:00

117 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

编程是一个先思考再编码的过程,思考是优于编码技能的,在思考过程中我们会考虑代码的可重用性、可靠性、更容易被他人理解,这时就会使用到[设计模式](https://so.csdn.net/so/search?q=%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F&spm=1001.2101.3001.7020)让代码编写工程化,这篇文章整理了设计模式的六大原则。
# SOLID
[SOLID: The First 5 Principles of Object Oriented Design | DigitalOcean](https://www.digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design "SOLID: The First 5 Principles of Object Oriented Design | DigitalOcean")
## 单一职责原则
单一职责原则Single Responsibility Principle
There should never be more than one reason for a class to change.(有且仅有一个原因可以引起类的变更)
不管让我干啥,我都只干一件事,你让我下楼取快递,顺便将垃圾带下去,对不起,我不干,我只取快递。
优点:
- 类的复杂性降低,实现什么职责都有清晰明确的定义;
- 可读性提高,复杂性降低,那当然可读性提高了;
- 可维护性提高,可读性提高,那当然更容易维护了;
- 变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。
## 接口隔离原则
接口隔离原则Interface Segregation Principle
1、Clients should not be forced to depend upon interfaces that they don't use.(客户端不应该依赖它不需要的接口)
2、The dependency of one class to another one should depend on the smallest possible interface.(类间的依赖关系应该建立在最小的接口上)
举个例子类A 通过 Interface1 依赖类B方法1方法2方法3类B 通过 Interface1 依赖D方法1方法4方法5看下未使用接口隔离原则和使用了接口隔离原则发生了什么变化。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy9nbzlqcEczQnVoVDBRRTJHbXR5Z1VEQ3dpYkliN01DMVNWSWljWnFzQTZRT3BrSG1mUEpnUTczS0picGVCZjRrOEd0dndOSXhyNWVvTmljZlphNFlhYm9JZy82NDA?x-oss-process=image/format,png)
## 依赖倒置原则
依赖倒置原则Dependence Inversion Principle
1、High level modules should not depend upon low level modules.Both should depend upon abstractions. (高层模块不应该依赖低层模块,两者都应该依赖其抽象)
2、Abstractions should not depend upon details. (抽象不应该依赖细节)
3、Details should depend upon abstractions. (细节应该依赖抽象)
举个例子类A 直接依赖 类B假如要将 类A 改为依赖 类C则必须通过修改 类A 的代码来达成。这种场景下类A一般是高层模块负责复杂的业务逻辑类B 和类C 是低层模块负责基本的原子操作假如修改类A会给程序带来不必要的风险。
解决方案:将 类A 修改为依赖 接口Interface1类B 和 类C 各自实现 接口Interface1类A 通过 接口Interface1 间接与 类B 或者 类C 发生联系,则会大大降低修改 类A 的几率。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy9nbzlqcEczQnVoVDBRRTJHbXR5Z1VEQ3dpYkliN01DMVM3emUxWURpYTVuUlkzWUZTaWJBc2FrVDlSUmdRUWJPMXhVdFFXNkNraWFzUTNJTGVrS0UydXVza0EvNjQw?x-oss-process=image/format,png)
## 里氏替换原则
里氏替换原则Liskov Substitution Principle
1、If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.(如果对每一个类型为 S 的对象o1都有类型为 T 的对象 o2使得以 T 定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 S 是类型 T 的子类型)
2、Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.(所有引用基类的地方必须能透明地使用其子类的对象)
举个例子,有一功能 P1由 类A 完成,现需要将功能 P1 进行扩展,扩展后的功能为 P其中 P 由原有功能 P1 与新功能 P2 组成,新功能 P 由 类A 的 子类B 来完成,则 子类B 在完成新功能 P2 的同时,有可能会导致原有功能 P1 发生故障。
解决方案当使用继承时遵循里氏替换原则。类B 继承 类A 时,除添加新的方法完成新增功能 P2 外,尽量不要重写 父类A 的方法也尽量不要重载父类A的方法。
继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些方法任意修改,就会对整个继承体系造成破坏,而里氏替换原则就是表达了这一层含义。
继承作为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能会产生故障。
优点:
- 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;
- 提高代码的重用性,可扩展性。
- 提高产品或项目的开放性。
## 迪米特法则
迪米特法则Law of Demeter
1、Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.(每个单元对于其他的单元只能拥有有限的知识:只是与当前单元紧密联系的单元)
2、Each unit should only talk to its friends; don't talk to strangers.(每个单元只能和它的朋友交谈:不能和陌生单元交谈)
3、Only talk to your immediate friends.(只和自己直接的朋友交谈)
举个例子,我们通过 `手机` 阅读 `微信读书 APP` 内的 `书籍`,如何设计类的编写?
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy9nbzlqcEczQnVoVDBRRTJHbXR5Z1VEQ3dpYkliN01DMVNpYnQ0a1NsRG12S0t5TWdodmZEekVJVW5WRm44eGxsOFRzZkRmMTNlSGhFZHRZWWpVR1hSb2tnLzY0MA?x-oss-process=image/format,png)
手机类 和 书籍类,这两个不能直接发生调用关系,需要 手机类 和 微信读书 APP 类先发生调用关系,然后微信读书 APP 类 再和 书籍 类可以发生调用关系,这样才遵循迪米特法则。
## 开闭原则
开闭原则Open Closed Principle
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.(软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的)
在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。
解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
## 小结
- 单一职责原则告诉我们实现类要职责单一;
- 接口隔离原则告诉我们在设计接口的时候要精简单一;
- 依赖倒置原则告诉我们要面向接口编程;
- 里氏替换原则告诉我们不要破坏继承体系;
- 迪米特法则告诉我们要降低耦合;
- 开闭原则是总纲,告诉我们要对扩展开放,对修改关闭;