一、耦合
耦合度是从模块外部考察模块的独立性,用来衡量模块间的相互联系。
主要考察:
- 模块相关联的代码和数据量,
- 模块间调用的方式
- 耦合的类型;
模块间耦合类型 | 含义 |
---|---|
独立耦合 | 无直接联系,仅共有上层模块 |
数据耦合 | 彼此交换数据(参数、返回值) |
控制耦合 | 通过参数控制执行逻辑分支 |
公共耦合 | 全局变量,公共数据/缓存 |
内容耦合 | 模块存在多入口(功能不单一)和非正常访问 |
低耦合建议:禁用内容耦合,限制公共耦合,少用控制耦合,推荐数据耦合
二、内聚
内聚是从功能角度来度量模块内的联系,它描述的是模块内的功能联系。
模块内聚程度 | 含义 |
---|---|
偶然内聚 | 仅为节省空间才把相同代码挪到一个模块 |
逻辑内聚 | 相似的功能组成一个新的模块 |
时间内聚 | 相同时间执行的功能组成一个模块 |
过程内聚 | 模块内的功能有执行时间顺序要求【中】 |
顺序内聚 | 内部功能之间彼此有逻辑依赖(输出->输入)【中】 |
通信内聚 | 模块内功能依赖共同的数据【中】 |
信息内聚 | 模块内的多个功能基于同一数据结构,每项功能有唯一入口 |
功能内聚 | 内部所有仅为实现某单一功能,无其他任何冗余 |
高内聚建议:遵循单一职责原则。
三、SOLID’原则
原则 | 含义 | 备注 |
---|---|---|
单一职责(SRP) | 一个类只负责一种功能 | 除非功能改变否则不予修改 |
开放封闭(OCP) | 对拓展开放,对修改关闭 | 降低加入新的逻辑分支的门槛 |
里氏替换(LSP) | 只有子类实例能够替换所有超类实例时,才是IS-A关系 | 否则用组合 |
接口分离(ISP) | 使用多个专门的接口,而非单一的总接口 | 不能强迫用户去依赖那些他们不使用的接口 |
依赖倒置(DIP) | 上层模块不依赖下层模块,二者都应依赖抽象 | 抽象不依赖实现,实现应依赖抽象 |
最小知识(LKP) | 最少知识原则,一个软件单元应当对其他软件单元有尽可能少的了解 | 迪米特法则:门面模式,中介者模式 |
四、类图中的依赖关系
类图反映各软件单元的组织结构和设计思路。
OOP中,类之间的关系如下:
类型 | 关系名 |
---|---|
IS-A | 泛化(继承),实现 |
HAS-A | 关联,聚合,组合 |
USE-A | 依赖 |
各关系的耦合程度:
下面是具体描述:
依赖(Dependence)
在类A中使用到了另外一个类B,这种使用时临时的、偶然的,代码表现为方法参数、局部变量、返回值,静态方法调用。设计中不应该有双向依赖。
关联(Association)
类A使用了类B,其中B是A的成员变量,但B在逻辑上并非类A的组成部分。
具体关系可划分为:单向关联,双向关联,自身关联。
聚合(Aggregation)
类A聚合类B,但类B在某种问题域下可以独立存在。
A可以不知道B的生命周期。
组合(Composition)
类A组合类B,但类B在某种问题域下不可以独立存在。
A必须知道B的生命周期。(要么A负责生成和释放B、或者B在生成和释放的时候通知A)
泛化(Generalization)
表现为一个类继承自另一个类(或者一个接口继承另一个接口)。
实现(Implementation)
表现为一个类实现一个或多个接口。
最后,
一张图表示类之间的依赖关系:
五、流程图
流程图从宏观上描述运行过程。
六、时序图
时序图反映调用关系。
七、状态图
状态图反映一个软件单元的动态行为。
八、思维导图
开发可以用思维导图来描述对需求的理解。
以上便是笔者日常文档中常用的图。
所谓:文不如表,表不如图。
恰当地使用图表来描述,不仅可以更好地理解代码,甚至可以在画图的过程中找到一些难以察觉的BUG。