参考:设计模式之美
设计模式学习导读
为什么要尽早学习并掌握设计模式
- 对面试中的设计模式相关问题
- 告别写被人吐槽的烂代码
- 提高复杂代码的设计和开发能力
- 让读源码、学框架事半功倍
- 为你的职场发展做铺垫
- 投资要趁早,这样我们才能尽早享受复利。同样,有些能力,要早点锻炼;有些东西,要早点知道;有些书,要早点读
如何评判代码质量
- 可维护性(maintainability):在不破坏原有代码设计、不引入新的 bug 的情况下,能够快速地修改或者添加代码
- 可读性(readability):code review 是一个很好的测验代码可读性的手段
- 可扩展性(extensibility):可扩展性实际是应用设计模式的最重要的目的
- 灵活性(flexibility):一段代码易扩展、易复用或者易用,我们都可以称这段代码写得比较灵活
- 简洁性(simplicity):思从深而行从简,真正的高手能云淡风轻地用最简单的方法解决最复杂的问题。这也是一个编程老手跟编程新手的本质区别之一
- 可复用性(reusability):减少重复代码的编写,复用已有的代码
- 可测试性(testability)
面向对象、设计原则、设计模式、编程范式、重构的关系
- 面向对象编程因为其具有丰富的特性(封装、抽象、继承、多态),可以实现很多复杂的设计思路,是很多设计原则、设计模式等编码实现的基础
- 设计原则是指导我们代码设计的一些经验总结,对于某些场景下,是否应该应用某种设计模式,具有指导意义。比如,“开闭原则”是很多设计模式(策略、模板等)的指导原则
- 设计模式是针对软件开发中经常遇到的一些设计问题,总结出来的一套解决方案或者设计思路。应用设计模式的主要目的是提高代码的可扩展性。从抽象程度上来讲,设计原则比设计模式更抽象。设计模式更加具体、更加可执行
- 编程规范主要解决的是代码的可读性问题。编码规范相对于设计原则、设计模式,更加具体、更加偏重代码细节、更加能落地。持续的小重构依赖的理论基础主要就是编程规范
- 重构作为保持代码质量不下降的有效手段,利用的就是面向对象、设计原则、设计模式、编码规范这些理论
面向对象
- 面向对象编程:是一种编程范式或编程风格。它以类或对象作为组织代码的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现的基石
- 面向对象编程语言:是支持类或对象的语法机制,并有现成的语法机制,能方便地实现面向对象编程四大特性(封装、抽象、继承、多态)的编程语言
封装、抽象、继承、多态解决的问题
封装屏蔽代码复杂性,对外提供接口而不需要外部了解实现细节 抽象是指在具体的事务上抽离出本质特征,能代表一类事务,抽象让代码更具有扩展性 继承是解决代码复用问题 多态是指代码运行过程的多样性
面向对象与面向过程对比
面向对象:程序设计以对象为主体,是一个或多个对象自身的方法组合实现程序 面向过程:程序设计以过程为主体,通过对数据的加工处理实现程序
哪些代码看似是面向对象,实际是面向过程
常见的简单的mvc代码,controller,service,dao,model层的代码包含很多对象,但每个对象都没有实际领域含义,没有领域方法,仅仅是set,get方法或者上下层代码的调用
接口vs抽象类
接口:java中interface定义接口,接口方法通常没有具体实现,接口通过implement来实现,一个接口可以继承多个接口 抽象类:抽象类用abstract方法定义,包含未实现的方法,子类通过extend继承,只能继承一个抽象类
接口:通常代表一种行为,不包含成员变量 抽象类:与非抽象类的区别是存在abstract方法,定义了一种规范
为什么基于接口而非实现编程
1、这条原则,可以将接口和实现相分离,封装不稳定的实现,暴露稳定的接口。上游系统面向接口而非实现编程,不依赖不稳定的实现细节,这样当实现发生变化的时候,上游系统的代码基本上不需要做改动,以此来降低耦合性,提高扩展性。 2、越抽象、越顶层、越脱离具体某一实现的设计,越能提高代码的灵活性,越能应对未来的需求变化。好的代码设计,不仅能应对当下的需求,而且在将来需求发生变化的时候,仍然能够在不破坏原有代码设计的情况下灵活应对。
为什么多用组合少用继承
1、继承过深的代码不便于阅读 2、继承容易违背里氏替换原则 3、组合可以解决继承爆炸问题 4、组合实现最少知识原则
基于贫血模型的MVC架构违背OOP吗
违背,但是要看是否合适,简单的mvc架构没必要使用oop的原则去实现
设计原则
单一职责原则
1、职责单一的功能,更加内聚,耦合更少 2、职责单一的功能,更方便复用 3、职责单一的功能,方便阅读理解 4、职责单一的功能,功能修改,影响可控
对扩展开放,对修改关闭
1、添加一个新的功能应该是,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。 2、对扩展开放、对修改关闭”的代码的关键是预留扩展点 3、开闭原则的使用不是免费的,要权衡变化与不变,并持续重构
里氏替换跟多态的区别
1、里氏替换原则是对继承的一种限制 2、继承是多态的一种实现方式 3、里氏替换原则:子类在用法和语义上要能替换所有父类出现的地方
接口隔离原则的三种应用
1、一组api接口:给客户端提供接口时,不要提供客户端不需要的接口 2、函数中的接口:函数功能要单一,不在一个函数中做多个事情 3、oop中的接口:接口职责要单一,不要让实现类实现不需要的功能
控制反转、依赖反转、依赖注入的区别和联系
1、控制反转:对象的创建由代码控制改为框架控制 2、依赖反转:又称为依赖倒置原则,指高层模块不依赖底层模块,他们共同依赖一个抽象,抽象不要依赖具体的实现,具体实现依赖抽象。 3、依赖注入:对象依赖的成员变量由框架创建并自动注入
KISS、YAGNI原则
1、kiss:keep it simple and stupid,保持简单,不写难以理解的;不写团队不熟悉的;不重复造轮子 2、yagni:You Ain’t Gonna Need It,不要做过度设计
DRY原则-提高代码的重用性
1、dry:don‘t retry yourself,不写重复代码
2、代码重复,但是代码重复并不一定违反 3、逻辑重复,相同逻辑的方法,定义多个 4、语义重复,逻辑上不会执行的代码
5、消除代码复用性: 减少代码耦合 满足单一职责原则 模块化 业务与非业务逻辑分离 通用代码下沉 继承、多态、抽象、封装 应用模板等设计模式
迪米特法则
The Least Knowledge Principle,最小知识原则 含义:不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口(也就是定义中的“有限知识”)
规范与重构
什么情况下要重构、重构什么、如何重构
1、重构可分为两类,持续重构和大的重构 2、持续重构在代码修改过程中,碰到不合理的地方,根据代码规范和设计原则,利用设计模式进行重构 3、大的重构往往设计框架上的大改动,需要专门抽出一整块时间进行重写,要做好单元测试 4、小步快跑的方式进行重构,一次不要改动太多内容,改动之后要进行测试
保证重构不出错的技术手段
- 单元测试
代码的可测试性
- 测试代码是否容易编写
- 提高代码可测试的方法
- 依赖注入,代替创建对象
- 减少未决行为
- 减少使用全局变量
- 静态方法难以mock
- 继承复杂会导致难以mock父类属性
- 耦合严重会导致,注入对象繁多