设计模式
2021-04-12 16:27:50 21 举报
AI智能生成
设计模式
作者其他创作
大纲/内容
一、代码质量评判标准
如何评价代码质量的高低
标准多,带有主观性,无法用单一的标准去评判,需要综合各个维度
最常用的评价标准有哪几个
最常用
可维护性
可扩展性
可读性
其他
灵活性
简洁性
可复用性
可测试性
如何才能写出高质量的代码
设计思想
设计原则
设计模式
编码规范
重构技巧
二、面向对象
面向对象概述
三种主流的变成范式(风格)
面向对象
面向过程
函数式编程
面向对象4大特性
封装
概念:也叫作信息隐藏或者数据访问保护
特点:需要变成需要提供权限访问控制语句支持;例如Java中的public、protected和private关键字
意义
保护数据不被修改、提高代码的可维护性
仅暴露有限的必要接口,提供类的易用性
抽象
概念:隐藏方法的具体实现
意义
修改实现不需要改变定义
处理复杂系统的有效手段,能有效过滤不必要关注的信息
继承
概念:表示类之间的is-a的关系
特性:编程语言需要提供特殊的语法机制来支持
2种模式
单继承:一个子类只能继承一个父类
多继承:一个子类可以继承多个父类
意义:解决代码复用性的问题
多态
概念:子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现
特性:变成语言需要提供特殊的语法机制来支持,例如继承、接口类和duck-typing
意义:提供代码的扩展性和复用性,是很多设计原则、设计模式和编程技巧代码实现的基础
面向对象vs面向过程
更能应对复杂类型的程序开发
具有更丰富的特性(封装、抽象、继承和多态)
更加人性化、更加高级和更加智能
面向对象分析、设计和编程
面向对象分析:搞清楚做什么,产出详细的需求描述
面向对象设计:搞清楚怎么做,将需要转化为具体的类
划分职责,进而识别有哪些类
定义类及属性和方法
定义类与类之间的交互关系
将类组装起来并提供执行入口
面向对象编程:将分析和设计的结果翻译成代码
接口vs抽象类
如果是表示一种is-a的关系,并且是为了解决代码复用性的问题,我们就用抽象类
如果是表示一种has-a的关系,并且是为了解决抽象而非代码复用性的问题,我们就用接口
基于接口非实现编程
1将接口和实现相分离,封装不稳定的实现,暴露稳定的接口
2即'基于抽象非实现编程'。抽象是提高代码扩展性、灵活性和可维护性的最有效手段之一
多用组合少用继承
为什么不推荐使用继承
继承层次过深、过复杂,影响代码的可维护性
组合相比继承有哪些优势
继承的3个作用
表示is-a的关系
支持多态特性
代码复用
组合解决层次过深、过复杂的继承关系影响代码可维护性的问题
如何判断该用继承还是组合
1类之间的继承结构稳定,层次比较浅、关系不复杂我们使用继承,反之使用组合
2鼓励多用组合少用继承,但组合并不是完美的,继承也并非一无是处
贫血模型vs充血模型
基于贫血模型的传统开发模式:面向过程
适合业务不复杂的系统开发
基于充血模型的DDD开发模式:面向对象
适合业务复杂的系统开发
两者主要的区别在service层,controller层和respository层的代码基本相同
三、设计原则
SOLID原则:SRP单一职责原则
概念:一个类只负责完成一个职责或者功能
意义
提高类的内聚性
实现代码的高内聚、松耦合
不满足的5种情况
1类中的代码行数、函数或者属性过多
2类依赖的其他类过多或者依赖类的其他类过多
3私有方法过多
4比较难给类起一个合适的名字
5类中大量方法都是集中操作类中的某几个属性
SOLID原则:OCP开闭原则
如何理解?
开闭原则不是完全杜绝修改,而是以最小修改代码的代价来完成新功能的开发
同样的代码改动,在粗代码粒度下,可能被认定为‘修改’,在细代码粒度下,可能有被认定为‘扩展’
如何做到?
时刻具备扩展意识,封装意识和抽象意识
常用来提高代码扩展性的方法
多态
依赖注入
基于接口非实现编程
大部分设计模式:装饰、策略、模板、职责链和状态
SOLID原则:LSP里氏替换原则
概念:子类对象能够替换程序中父类对象出现的任何地方,并且保证原来的程序的逻辑行为不变及正确性不背破坏
核心:"disign by contract ,按照协议来设计",父类定义了函数的约定(或协议),子类可以改变函数内部的实现逻辑,但不能改变函数原有的约定。
里氏替换原则vs多态
多态是面向对象编程的一大特性,是面向对象编程的一种语法,是一种代码实现的思路
里氏替换是一种设计原则,用来指导继承关系中子类该如何设计,子类设计要保证在替换父类的时候,不改变原程序的逻辑及不破坏原有程序的正确性
多态是面向对象编程的一大特性,是面向对象编程的一种语法,是一种代码实现的思路
SOLID原则:ISP接口隔离原则
概念:客户端不应该强迫依赖它不需要的接口,其中的“客户端”,可以理解为接口的调用者或使用者
核心:“接口”的三种不同理解
1把“接口”理解为一组接口的集合
2把“接口”理解为单个接口或者函数
3把“接口”理解为OOP中的接口
单一职责原则vs接口隔离原则
单一职责原则针对的是模块,类,接口的设计
接口隔离原则提供了一种判断接口职责是否单一的标准:通过调用者中如何使用来间接判定
SOLID原则:DIP依赖倒置原则
控制翻转:一个比较笼统的设计思想,并不是一种具体的实现方法
依赖注入:和控制翻转刚好想法,是一种具体的编码技巧
依赖注入框架:通过依赖注入提供的扩展点,简单配置下所有需要的类及其类与类之前的依赖关系,就可以实现由框架自动创建对象,管理对象的生命周期,依赖注入等原本需要程序员做的事情
依赖翻转原则:也叫作依赖倒置原则,和控制翻转有点类似,主要用来指导框架层面的设计
KISS、YAGNI原则
KISS原则
概念:尽量保持简单
意义:保持代码可读和可维护的重要手段
如何写出满足KISS原则的代码
不要使用同事可能不懂的技术实现代码
不要重复造轮子,善于使用已经有的工具类库
不要过度优化
YAGNI原则
概念:不要设计当前用不到的功能,不要去编写当前用不到的代码
核心:不要过度设计
KISS vs YAGNI
KISS原则:“如何做”,尽量保持简单
YAGNI原则: “要不要做”,当前不需要要的就不要做
DRY原则
概念:不要写重复的代码
3种代码重复的情况
实现逻辑重复
功能语义重复
代码执行重复
提高代码复用性的一些手段
减少代码耦合
满足单一职责原则
模块化
业务与非业务逻辑分离
通用代码下沉
继承、多态、抽象、封装
应用模板等设计模式
有复用意识
LOD原则
高内聚、松耦合
高内聚:指导类本身的设计。松耦合:指导类与类依赖关系的设计
高内聚:相近的功能应该放在同一个类中,不相近的功能不要放在同一个类中
松耦合:即使两个类有依赖关系,一个类的代码改动也不会或者很少导致依赖类代码的改动
迪米特法则
概念:不该有直接依赖关系的类之间,不要又依赖;有依赖的类之间,尽量只依赖必要的接口
意义:减少类之间的耦合,让类越独立越好
四、规范与重构
重构概述
为什么要重构? 保持代码质量持续处于一个可控状态,不至于腐化到无可救药的地步
重构什么?
大规模高层次的重构
内容:对代码分层、模块化、解耦、梳理类之间的交互关系、抽象复用组件
理论基础:设计思想、原则、模式
小规模低层次的重构
内容: 规范命名、注释、修正函数参数过多、消除超大类、提取重复代码等编程细节问题,主要是针对类、函数级别的重构
理论基础: 编码规范
什么时候重构? 建立持续重构意识,把重构作为开发必不可少的部分融入到开发中
如何重构?
大规模高层次的重构 难度比较大,需要有组织、有计划地进行,分阶段地小步快跑
小规模低层次的重构 影响范围小,改动耗时短,随时随地都可以去做
单元测试
什么是单元测试?
概念:代码层面的测试,用于测试“自己”编写的代码的逻辑正确性
“单元”:一般是类或函数,而不是模块或者系统
为什么要写单元测试?
能有效地发现代码中的 Bug、代码设计上的问题
写单元测试的过程本身就是代码重构的过程
单元测试是对集成测试的有力补充,能帮助我们快速熟悉代码,是 TDD 可落地执行的折中方案
如何编写单元测试?
概念:针对代码设计覆盖各种输入、异常、边界条件的测试用例,并将其翻译成代码的过程
方法:可以利用一些测试框架来简化测试代码的编写
5个正确的编写认知
1编写单元测试尽管繁琐,但并不是太耗时
2我们可以稍微放低单元测试的质量要求
3覆盖率作为衡量单元测试好坏的唯一标准是不合理的
4写单元测试一般不需要了解代码的实现逻辑
5单元测试框架无法测试多半是代码的可测试性不好
单元测试为何难落地执行?
写单元测试本身比较繁琐,技术挑战不大,很多程序员不愿意去写
国内研发比较偏向“快糙猛”,容易因为开发进度紧,导致单元测试的执行虎头蛇尾
没有建立对单元测试的正确认识,觉得可有可无,单靠督促很难执行得很好
代码的可测性
什么是代码的可测性? 针对代码编写单元测试的难易程度
依赖注入是编写可测试性代码的最有效手段
依赖注入可以通过 mock 的方法将不可控的依赖变得可控
除了 mock 方式,我们还可以利用二次封装来解决某些代码行为不可控的情况
常见的5种 Anti-Patterns
1代码中包含未决行为逻辑
2滥用可变全局变量
3滥用静态方法
4使用复杂的继承关系
5高度耦合的代码
大型重构:解耦
“解耦”为何如此重要?
保证代码松耦合、高内聚,是控制代码复杂度的有效手段
代码高内聚、松耦合,也就是意味着,代码结构清晰、分层、模块化合理、依赖关系简单、模块或类之间的耦合小,那代码整体的质量就不会差
代码是否需要“解耦”?
间接的衡量标准
改动一个模块或类的代码受影响的模块或类是否有很多
改动一个模块或者类的代码依赖的模块或者类是否需要改动
代码的可测试性是否好
直接的衡量标准 把模块与模块之间及其类与类之间的依赖关系画出来,根据依赖关系图的复杂性来判断
如何给代码“解耦”?
封装与抽象
中间层
模块化
其他设计思想与原则
单一职责原则
基于接口而非实现编程
依赖注入
多用组合少用继承
迪米特法则
设计模式
小型重构:编码规范(20条)
命名与注释
1命名的关键是能准确的达意
2借助类的信息来简化属性、函数的命名,利用函数的信息来简化函数参数的命名
3命名要可读、可搜索。不要使用生僻的、不好读的英文单词来命名
4接口有两种命名方式。一种是在接口中带前缀"I",另一种是在接口的实现类中带后缀“Impl”。抽象类的命名,我们更倾向于带有前缀“Abstract”
5注释的内容:做什么、为什么、怎么做。复杂的类和接口,我们还需要写明“如何用”
6类和函数一定要写注释,而且要写的尽可能全面详细些
代码风格
7函数代码行数不要超过一屏幕的大小,比如50行
8一行代码最好不要超过IDE的显示宽度
9善用空格分割单元块
10推荐2格缩进,节省空间。一定不要用tab键缩进
11将大括号跟上一条语句同行,可以节省代码行数,另起新的一行,接口清晰
12类中先写成员变量后写函数。成员变量之间或函数之间,先写静态成员变量或函数,后写普通变量或函数,并且按照作用域大小依次排列
编程技巧
13将复杂的逻辑提炼拆分成函数和类
14通过拆分成多个函数的方式来处理参数过多的情况
15通过将参数封装为对象来处理参数过多的情况
16函数中不要使用参数来做代码执行逻辑的控制
17移除过深的嵌套层次,方法包括:去掉多余的 if 或 else 语句,使用 continue、break、return 关键字提前退出嵌套,调整执行顺序来减少嵌套,将部分嵌套逻辑抽象成函数
18用字面常量取代魔法数
19利用解释性变量来解释复杂表达式
20项目、团队,甚至公司,一定要制定统一的编码规范,并且通过 Code Review 督促执行
五、创建型设计模式
意义:主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码
常用类型
1. 单例模式
2. 工厂模式
3. 建造者模式
不常用类型
4. 原型模式
六、结构型设计模式
意义:主要总结了一些类或对象组合在一起的经典结构,这些经典的结构可以解决特定应用场景的问题
常用类型
1. 代理模式
2. 桥接模式
3. 装饰器模式
4. 适配器模式
不常用类型
5. 门面模式
6. 组合模式
7. 享元模式
七、行为型设计模式
意义:解决的就是“类或对象之间的交互”问题
常用类型
1. 观察者模式
2. 模板模式
3. 策略模式
4. 职责链模式
5. 迭代器模式
6. 状态模式
不常用类型
7. 访问者模式
8. 备忘录模式
9. 命令模式
10. 解释器模式
11. 中介模式
0 条评论
下一页