设计模式上篇之设计原则与思想
# 面向对象
# 四大特性
1. 抽象
2. 封装
3. 继承
4. 多态:同一操作作用于不同对象会产生不同的效果,一般是通过继承来实现
# 接口 VS 抽象类
接口
- 只有方法,没有成员变量
- 方法没有实现,也称为抽象方法
- 类实现接口时必须实现接口中的所有方法(抽象方法)
- 类可以实现多个接口(很像多继承)
抽象类
- 可以有方法,也可以有成员变量
- 方法可以有实现,也可以没有实现
- 抽象类不能被实例化,只能被继承
- 子类继承抽象类必须实现抽象类中的所有抽象方法(未实现的方法)
# 设计思想
1. 基于接口而非实现编程
2. 多用组合少用继承
# 贫血模型 VS 充血模型
贫血模型
- 类中只有数据没有方法,是典型的面向过程编程方式
充血模型
- 类中即有数据也有方法,是典型的面向对象编程方式
# 面向对象开发主要流程
1. 面向对象分析(OOA Object Oriented Analysis):产出详细的需求描述
2. 面向对象设计(OOD Object Oriented Design):根据需求描述产出类
3. 面向对象编程(OOP Object Oriented Programming):基于对象编码实现
# 设计原则
剑圣独孤求败的绝招是什么,没有招,无招胜有招 就算不知道设计模式,如果理解了设计原则,也能写出 高内聚、低耦合 的好代码 设计模式只是前人把优化过程中常见的问题和解决方法加以总结,方便后人参考而已 在项目的实际开发过程中不要过于教条主义,要根据实际的应用场景进行设计原则的选择,甚至创造出独属于那个应用场景的新的设计原则
# SOLID 原则
- 单一职责原则 (SRP Single Responsibility Principle)
- 一个类或者模块只负责完成一个职责(或者功能)
- 开闭原则(OCP Open Closed Principle)
- 对扩展开放(提高代码的可扩展性)
- 对修改关闭(提高代码的稳定性)
- 里氏替换原则(LSP Liskov Substitution Principle)
- 子类完美继承父类
- 子类的实现不能替换原有父类的逻辑,但可以对功能做扩展(有点类似于开闭原则呀)
- 接口隔离原则(ISP Interface Segregation Principle)
- 接口的调用者不应该被强迫依赖它不需要的接口
- 对接口的理解
- 一组 API 接口集合
- 单个接口或函数
- OOP 中的接口
- 依赖反转原则(DIP Dependency Inversion Principle)
- 控制反转(IOC Inversion Of Control)
- 依赖注入(DI Dependency Injection)
- 算是解耦的一种方式
- 提高了代码的可扩展性
- 依赖反转原则也叫依赖倒置原则
- 高层模块不依赖底层模块,它们共同依赖同一个抽象
- 抽象不依赖具体实现细节,具体实现细节依赖抽象
# LOD 原则
Law Of Demeter
也叫最小知识原则,强调的是类之间的依赖,也就是要 松耦合,符合高内聚、低耦合的设计思想
- 不该有直接依赖关系的类之间,不要有依赖
- 有依赖关系的类之间,尽量只依赖必要的接口
# KISS 原则
Keep It Simple and Stupid
保持尽量简单
# YAGNI 原则
You Ain't Gonna Need It
不要过度设计,不需要的不要做
# DRY 原则
Don't Repeat Yourself
不要写重复的代码
这里的重复体现在:
- 实现逻辑重复
- 功能语义重复
- 代码执行重复
实现逻辑重复,但功能语义不重复的代码,并不违反 DRY 原则
实现逻辑不重复,但功能语义重复的代码,也算是违反 DRY 原则
代码执行重复也算是违反 DRY 原则
# 代码重构
# 四要素
- 目的:为什么要重构?
- 提高代码的 可读性、可维护性
- 对象:对什么进行重构?
- 大重构:针对 系统、模块、代码结构、类之间关系 等,使用解耦等技巧
- 小重构:针对 类,函数 等,使用编程规范等技巧即可
- 时机:何时进行重构?
- 持续重构
- 开发阶段、维护阶段等各个阶段都需要进行重构
- 方法:如何进行重构?
- 编写单元测试
- 编写可测试性代码
- 使用解耦方法(实现高内聚、松耦合)
- 封装与抽象
- 中间层
- 模块化
- 其它设计原则与思想
# 编程规范
# 命名
命名的关键是能准确达意。对于不同作用域的命名,我们可以适当地选择不同的长度。
我们可以借助类的信息来简化属性、函数的命名,利用函数的信息来简化函数参数的命名。
命名要可读、可搜索。不要使用生僻的、不好读的英文单词来命名。命名要符合项目的统一规范,也不要用些反直觉的命名。
接口有两种命名方式:一种是在接口中带前缀“I”;另一种是在接口的实现类中带后缀“Impl”。对于抽象类的命名,也有两种方式,一种是带上前缀“Abstract”,一种是不带前缀。这两种命名方式都可以,关键是要在项目中统一。
# 注释
注释的内容主要包含这样三个方面:做什么(What)、为什么(Why)、怎么做(How)。对于一些复杂的类和接口,我们可能还需要写明“如何用”。
类和函数一定要写注释,而且要写得尽可能全面详细。函数内部的注释要相对少一些,一般都是靠好的命名、提炼函数、解释性变量、总结性注释来提高代码可读性。
# 代码风格
函数、类多大才合适?函数的代码行数不要超过一屏幕的大小,比如 50 行。类的大小限制比较难确定。
一行代码多长最合适?最好不要超过 IDE 的显示宽度。当然,也不能太小,否则会导致很多稍微长点的语句被折成两行,也会影响到代码的整洁,不利于阅读。
善用空行分割单元块。对于比较长的函数,为了让逻辑更加清晰,可以使用空行来分割各个代码块。
四格缩进还是两格缩进?我个人比较推荐使用两格缩进,这样可以节省空间,尤其是在代码嵌套层次比较深的情况下。不管是用两格缩进还是四格缩进,一定不要用 tab 键缩进。
大括号是否要另起一行?将大括号放到跟上一条语句同一行,可以节省代码行数。但是将大括号另起新的一行的方式,左右括号可以垂直对齐,哪些代码属于哪一个代码块,更加一目了然。
类中成员怎么排列?在 Google Java 编程规范中,依赖类按照字母序从小到大排列。类中先写成员变量后写函数。成员变量之间或函数之间,先写静态成员变量或函数,后写普通变量或函数,并且按照作用域大小依次排列。
# 编码技巧
将复杂的逻辑提炼拆分成函数和类。
通过拆分成多个函数或将参数封装为对象的方式,来处理参数过多的情况。
函数中不要使用参数来做代码执行逻辑的控制。
函数设计要职责单一。
移除过深的嵌套层次,方法包括:去掉多余的 if 或 else 语句,使用 continue、break、return 关键字提前退出嵌套,调整执行顺序来减少嵌套,将部分嵌套逻辑抽象成函数。
用字面常量取代魔法数。
用解释性变量来解释复杂表达式,以此提高代码可读性。