SOLID Pattern
SOLID Pattern是开发clean code的一个基本的理论指导, 它可以帮助开发人员实现具有较高可读性,可维护性以及鲁棒性的代码, 是每一个开发人员应该掌握的知识。
Single Responsibility Principle(单一功能原则)
1 | A class should have one and only one reason to change, |
一个模块(方法,类,组件等)应该只有一个职能, 避免因为多职能间的耦合, 比如状态的耦合,职能实现的耦合等, 导致一个职能发生修改时,其他的职能受到影响。
单一功能原则可能是里面最重要的一个原则,它帮助定义模块的职能边界, 需要注意以下几个方面:
- 基于现有的业务进行分析,明确模块职能的边界,尽量保证一个模块只有一个职能
- 避免职能的耦合
- 避免过度设计,基于现有的业务进行设计, 当业务变更时,原本的设计无法满足时,再对设计进行优化
1 | // bad code |
优化方案:
- 对职能进行分离,提取到不同的类中
- 通过组合不同职能的类来实现业务逻辑
Open-Closed Principle(开闭原则)
1 | Objects or entities should be open for extension but closed for modification. |
对相同职能进行扩展时,不应该修改到原有的职能,否则就有可能引入新的bug。
开闭原则主要针对具有相同职能,但不同实现的场景,为了避免因为切换不同实现到引发新的bug。
如何实现开闭原则:
- 对职能进行分析,确定分类并提取共有职能,抽象出相关的接口
- 每个种类实现对应的接口
- 业务逻辑基于接口来实现职能
- 当有新的种类时,只需要针对新的种类实现相应的接口即可,而不用不修改原有的业务逻辑
1 | /** |
优化方案:
- 提取共同的职能---计算面积,抽象出相关的接口
- 每个形状实现各自的职能接口
- 业务逻辑基于职能接口进行实现
Liskov Substitution Principle(里氏替换原则)
1 | Functions that use pointers or references to base classes |
子类必须遵循父类的行为和约束。
尤其是要注意约束,只有完全满足下面这些约束才能进行替换:
- 入参的约束, 包括作用域,格式,类型等
- 出参的约束,包括值域,格式,类型等
- 异常的约束,包括类型,数量等
- 行为逻辑的约束,指该行为应该要实现怎样的职能
如何实现里氏替换原则:
- 重新定义父类的职能
- 将不符合父类的职能的实现从子类中移出
1 | /** |
优化方案:
将不属于Shape定义的职能移到外面去,保证子类完全满足父类的行为和约束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class GoodPractice {
public static void main(String[] args) {
Shape shape = new Rectangle();
double length = 1.3;
double width = 2.3;
checkFormat(length);
checkFormat(width);
Double area = shape.area(length, width);
System.out.println(area);
double length1 = 1.322;
double width1 = 2.333;
checkFormat(length1);
checkFormat(width1);
area = shape.area(length1, width1);
System.out.println(area);
}
private static void checkFormat(Double value) throws NumberFormatException {
String s = Double.toString(value);
int index = s.indexOf('.');
int length = s.substring(index + 1).length();
if (length > 2) {
throw new NumberFormatException();
}
}
}
Interface Segregation Principle(接口隔离原则)
1 | Clients should not be forced to depend upon interfaces that they do not use. |
模块所依赖的接口,只应提供模块所需的职能,不应该暴露额外用不到的职能; 如果有暴露额外的职能,则需要对接口进行重新设计。
1 | /** |
优化方案:对接口的职能进行拆分
1 | public class GoodPractice { |
Dependency Inversion Principl(依赖反转原则)
1 | Depend upon abstractions, not concretions |
高层模块不应依赖下层模块的实现,而应该依赖于下层模块的抽象。
1 | /** |
重构方案:
- 提取抽象接口
- 高层模块依赖于抽象接口进行实现
1 | public class GoodPractice { |