高内聚、低耦合
内聚: 高内聚是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常说的单一责任原则 。模块的内聚反映模块内部联系的紧密程度 。一个模块只需做好一件事件,不要过分关心其他任务。高内聚性的好处是可以提高程序的可靠性。
耦合: 指软件系统结构中各模块间相互联系紧密程度 的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。
单一职责原则
1. 如何理解单一职责原则(SRP) ?
一个类只负责完成一个职责或者功能。不要设计大而全的类,要设计粒度小、功能单一的 类。 单一职责原则是为了实现代码高内聚、低耦合 ,提高代码的复用性、可读性、可维护 性。
2. 如何判断类的职责是否足够单一?
不同的应用场景、不同阶段的需求背景、不同的业务层面,对同一个类的职责是否单一,可能会有不同的判定结果。 实际上,一些侧面的判断指标更具有指导意义和可执行性, 比如,出现下面这些情况就有可能说明这类的设计不满足单一职责原则:
- 类中的代码行数、函数或者属性过多;
- 类依赖的其他类过多,或者依赖类的其他类过多;
- 私有方法过多;
- 比较难给类起一个合适的名字;
- 类中大量的方法都是集中操作类中的某几个属性。
实际设计方式 可以先写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类。这就是所谓的持续重构
3. 类的职责是否设计得越单一越好?
单一职责原则是为了提高类的内聚性,减少了代码的耦合性。 但是,如果拆分得过细,实际上会适得其反,反倒会降低内聚性,也会影响代码的可维护性。
开闭原则
定义: 对扩展开放,对修改关闭
对扩展开放是为了应对需求变化, 对修改关闭是为了保证已有代码的稳定性;
1. 如何理解“对扩展开放、对修改关闭”?
添加一个新的功能,应该是通过在已有代码基础上扩展代码 (新增模块、类、方法、属性等),而非修改已有代码 的方式来完成。关于定义,我们有两点要注意。
- 第一点是,开闭原则并不是说完全杜绝修改,而是以最小的修改代码的代价来完成新功能的开发。
- 第二点是,同样的代码改动,在粗代码粒度下,可能被认定为“修改”;在细代码粒度下,可能又被认定为“扩展”。
2. 如何做到“对扩展开放、修改关闭”?
我们要时刻具备扩展意识、抽象意识、封装意识 。在写代码的时候,我们要多花点时间思考一下,这段代码未来可能有哪些需求变更,如何设计代码结构,事先留好扩展点,以便在未来需求变更的时候,在不改动代码整体结构、做到最小代码改动的情况下,将新的代码灵活地插入到扩展点上。
很多设计原则、设计思想、设计模式,都是以提高代码的扩展性为最终目的的。特别是 23种经典设计模式 ,大部分都是为了解决代码的扩展性问题而总结出来的,都是以开闭原则为指导原则的。
最常用来提高代码扩展性的方法有:多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式(比如,装饰、策略、模板、职责链、状态)。
里氏替换原则
定义: 子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。 这么看来里氏替换和多态 类似。但其实二者是两回事;
继承在给程序设计带来便利的同时,也带来了弊端:
- 使用继承会给程序带来侵入性,
- 程序的可移植性降低,
- 增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障
那该如何正确的使用继承? 里氏替换原则:所有引用父类的地方,必须能使用其子类的对象替换,且代码逻辑不受影响。
接口隔离原则
理解“接口隔离原则” 的重点是理解其中的“接口”二字。这里有三种不同的理解。
-
如果把“接口”理解为一组接口集合,可以是某个微服务的接口,也可以是某个类库的接口等。如果部分接口只被部分调用者使用,我们就需要将这部分接口隔离出来,单独给这部分调用者使用,而不强迫其他调用者也依赖这部分不会被用到的接口。 -
如果把“接口”理解为单个 API 接口或函数,部分调用者只需要函数中的部分功能,那我们就需要把函数拆分成粒度更细的多个函数,让调用者只依赖它需要的那个细粒度函数。 -
如果把“接口”理解为 OOP 中的接口,也可以理解为面向对象编程语言中的接口语法。那接口的设计要尽量单一,不要让接口的实现类和调用者,依赖不需要的接口函数。
接口隔离原则与单一职责原则的区别
单一职责原则针对的是模块、类、接口 的设计。接口隔离原则相对于单一职责原则,一方面更侧重于接口的设计,另一方面它的思考角度也是不同的。
接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。
依赖反转原则
依赖反转原则跟控制反转IOC 有点类似,按IOC来理解
要能清楚的回答出:“依赖反转”这个概念指的是“谁跟谁”的“什么依赖”被反转了?“反转”两个字该如何理解?
迪米特法则
不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。迪米特法则是希望减少类之间的耦合,让类越独立越好。每个类都应该少了解系统的其他部分。一旦发生变化,需要了解这一变化的类就会比较少。
DRY原则
我们今天讲了三种代码重复的情况:实现逻辑重复、功能语义重复、代码执行重复。实现逻辑重复,但功能语义不重复的代码,并不违反 DRY 原则。实现逻辑不重复,但功能语义重复的代码,也算是违反 DRY 原则。除此之外,代码执行重复也算是违反 DRY 原则。
|