这是我参与「第五届青训营 」伴学笔记创作活动的第 15 天
设计模式概述
模式是在特定环境下人们解决某类重复出现问题的一套成功或有效的解决方案。
设计模式是在特定环境下为解决某一通用软件设计问题提供的一套定制的解决方案,该方案描述了对象和类之间的相互作用。
设计模式是一套被反复使用、多数人知晓的、经过分类别的代码设计经验总结
目的:为了可重用代码、让代码更容易被他人理解、提高代码可靠性
设计模式的组成:模式名称、问题、解决方案、效果
分类:
- 根据目的分类:创建型模式(创建对象)、结构性模式(处理类和对象的组合)、行为型模式(描述类和对象如何交互和怎么分配职责)
- 根据范围分类:类模式(处理类和子类之间的关系)、对象模式(处理对象之间的关系)
面向对象概述
面向对象优点:可维护、可复用、可扩展、灵活性高
面向对象编程特性:封装、继承、多态
衡量软件质量的属性:
- 可维护性:指软件能够被理解、改正、适应及扩展的难易程度
- 可复用性:指软件能够被重复使用的难易程度
目标:
- 实现设计方案或者源代码的复用
- 确保系统易于扩展和修改
- 具有良好的可维护性
面向对象设计原则:
-
单一职责原则
一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中
-
开闭原则(总原则)
软件实体应当对扩展开放,对修改关闭
-
里氏代换原则
任何父类出现的地方,子类都可以替代出现
-
依赖倒转原则
要依赖于抽象,不要依赖于具体的实现。抽象不应该依赖于细节,细节应该依赖于抽象
-
接口隔离原则
客户端不应该依赖那些它不需要的接口
-
合成复用原则
优先使用对象组合,而不是继承来达到复用的目的
-
迪米特法则
又称最少知识原则,一个对象应当对其他对象尽可能少的了解,不和陌生人说话
单一职责原则
描述:
- 一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。
- 就一个类而言,应该仅有一个引起它变化的原因
分析:
- 一个类职责越多,复用的可能性就越小
- 当一个职责变化时,可能导致对象状态变化,从而影响其他职责的工作。因此,应该将不同的职责封装到不同的类中
- 单一职责原则是实现高内聚,低耦合的指导方针
好处:
- 类的复杂性降低,职责明确
- 可读性、可维护性高
使用场景:接口设计、类的设计、方法的设计
开闭原则(总原则)
描述:
- 软件实体应当对扩展开放,对修改关闭。
- 即开放-封闭原则,软件实体(类、模块、函数等)应该可以扩展,但是不能修改。
分析:
- 软件实体可以是一个软件模块、一个由多个类组成的局部结构或一个独立的类
- 开闭原则是指软件实体应尽量在不修改原有代码的情况下进行扩展,面对需求,对程序的改动是增加新代码,不是更改现有代码。
- 稳定的抽象层,灵活的实现层
- 找到系统可变的因素,将其封装起来
里氏代换原则
继承优点:
- 代码共享,可以减少类的工作量
- 提高代码复用
- 子类可以对父类的方法进行重写
- 提高代码的可扩展性
继承缺点:
- 继承是入侵式的
- 降低代码的灵活性
- 增强了耦合性
- Java类单一继承(接口支持多继承),C++类多重继承
描述:
- 如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型。
- 所有引用基类的地方必须能透明地使用其子类的对象
分析:
- 将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常
- 尽量使用基类类型来对对象进行定义
- 运用时,父类应该是抽象类或者接口,让子类继承父类或者实现父类
- 无需修改原有类型的代码。易于通过新增的方式来扩展系统功能
依赖倒置原则(面向接口编程)
描述:
- 高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
- 面向接口编程
分析:
- 尽量在传递参数或者关联关系中,传递层次高的抽象类或者接口
- 编程的过程中,尽量使用抽象层进行编程
- 针对抽象层编程,具体类通过依赖注入的方式注入到其他对象。构造注入、设值注入(Setter)、接口注入
接口隔离原则
描述:
- 客户端不应该依赖那些它不需要的接口。
- 类间的依赖关系应该建立在最小的接口上。
分析:
-
接口细化,如果接口内容过于庞杂,需要将其划分为更小的
-
每一个接口承担相对独立的角色
-
接口隔离 VS 单一职责
- 单一职责更加重视类的实现上的单一,面向开发者,注重业务逻辑的职责划分
- 接口隔离更多面向使用者,隔离要求接口方法尽可能精炼
接口隔离原则:
- 接口要尽量小,不臃肿
- 接口要高内聚,减少对外的交互
- 定制服务。为每一类(某个接口)的使用者提供单独的服务
- 接口的设计是有限度的。细粒度也并不是越小越好
合成复用原则
也成为,”组合\聚合原则“。
描述:
- 优先使用对象组合,而不是通过继承来达到复用的目的
分析:
-
合成复用原则就是在一个新对象里面通过关联关系或者组合关系来使用一些对象,使之成为新对象的一部分,达到复用的目的
-
新对象通过委派调用已有对象的方法达到复用功能的目的
-
复用时要尽量使用组合/聚合关系(关联关系),少用继承
-
继承复用 VS 组合/聚合复用
- 继承复用:实现简单,易于扩展。破坏系统的封装性;继承来的实现是静态的,不可能在运行时改变;没有足够的灵活性
- 组合/聚合复用:耦合度相对较低,有选择性的调用成员对象的操作。
Coda法则;
- 子类是基类的一个特殊种类(is-a),而不是基类的一个角色(has-a)
- 永远不会出现永远不会出现将子类替换成另一个类的子类的情况
- 子类具有扩展基类的责任,而不是去置换(Override)
- 只有在分类学上有意义时,才可以使用继承
迪米特法则
描述:
- 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
分析:
- 每一个软件实体应该尽可能少的与其他实体发生相互作用
- 一个对象应该对其他对象应该有最少的了解(接口隔离)
- 一个类对其他自己需要耦合或者调用的类应该知道的尽量少
- 应用迪米特法则可以降低系统的耦合度
创建型模式
- 将对象的创建过程和使用过程进行分离
- 创建型模式更加关注对象的创建过程
- 创建型模式,更加符合单一职责,外界只需要知道他们的共同接口即可