代码质量
# 如何发现代码质量问题?
# 常规 checklist
- 目录设置是否合理、模块划分是否清晰、代码结构是否满足“高内聚、松耦合”?
- 是否遵循经典的设计原则和设计思想(SOLID、DRY、KISS、YAGNI、LOG)?
- 设计模式是否应用得当?是否有过度设计?
- 代码是否容易扩展?如果要添加新功能,是否容易实现?
- 代码是否可以复用?是否可以服用已有的项目代码或类库?是否有重复造轮子?
- 代码是否容易测试?单元测试是否全面覆盖了各种正常和异常的情况?
- 代码是否易读?是否符合编码规范?
# 业务需求
- 代码是否实现了预期的业务需求?
- 逻辑是否正确?是否处理了各种异常情况?
- 日志打印是否得当?是否方便 debug 排查问题?
- 接口是否易用?是否支持幂等、事务等?
- 代码是否存在并发问题?是否线程安全?
- 性能是否有优化空间,比如 SQL、算法是否可以优化?
- 是否有安全漏洞?比如输入输出校验是否全面?
# 程序出错返回啥?
# 返回错误码
- C 语言没有异常这样的语法机制,返回错误码便是最常用的出错处理方式。
- Java、Python 等比较新的编程语言,大部分情况下,我们都用异常来处理函数出错的情况,极少会用到错误码。
# 返回 Null 值
- 在多数编程语言中,我们用 NULL 来表示“不存在”这种语义。
- 对于查找函数来说,数据不存在并非一种异常情况,是一种正常行为,所以返回表示不存在语义的 NULL 值比返回异常更加合理。
- 当然在 Java8 之后我们可以使用 Optional 进行空判断。
# 返回空对象
- 返回 NULL 值有各种利弊,对此有一个比较经典的应对策略,那就是应用空对象设计模式。
- 当函数返回的数据是字符串类型或者集合类型的时候,我们可以用空字符串或空集合替代 NULL 值,来表示不存在的情况,这样我们可以不用做 NULL 值判断。
# 抛出异常对象
- 最常用的函数出错处理方式是抛出异常,异常有两种类型:受检异常和非受检异常。
- 对于应该用受检异常还是非受检异常,我们需要根据团队的开发习惯,在同一个项目中,制定统一的异常处理规范即可。
- 对于函数抛出的异常,我们有三种处理方法:
- 直接吞掉
- 直接往上抛出
- 包裹成新的异常抛出
- 对于是否向上抛出异常,要看上层代码是否关系这个异常。
# 真实案例
public class RandomIdGenerator implements IdGenerator {
private static final Logger logger = LoggerFactory.getLogger(RandomIdGenerator.class);
@Override
public String generate() throws IdGenerationFailureException {
String substrOfHostName = null;
try {
substrOfHostName = getLastFieldOfHostName();
} catch (UnknownHostException e) {
throw new IdGenerationFailureException("...", e);
}
long currentTimeMillis = System.currentTimeMillis();
String randomString = generateRandomAlphameric(8);
String id = String.format("%s-%d-%s",
substrOfHostName, currentTimeMillis, randomString);
return id;
}
private String getLastFieldOfHostName() throws UnknownHostException{
String substrOfHostName = null;
String hostName = InetAddress.getLocalHost().getHostName();
if (hostName == null || hostName.isEmpty()) {
throw new UnknownHostException("...");
}
substrOfHostName = getLastSubstrSplittedByDot(hostName);
return substrOfHostName;
}
@VisibleForTesting
protected String getLastSubstrSplittedByDot(String hostName) {
if (hostName == null || hostName.isEmpty()) {
throw new IllegalArgumentException("...");
}
String[] tokens = hostName.split("\\.");
String substrOfHostName = tokens[tokens.length - 1];
return substrOfHostName;
}
@VisibleForTesting
protected String generateRandomAlphameric(int length) {
if (length <= 0) {
throw new IllegalArgumentException("...");
}
char[] randomChars = new char[length];
int count = 0;
Random random = new Random();
while (count < length) {
int maxAscii = 'z';
int randomAscii = random.nextInt(maxAscii);
boolean isDigit= randomAscii >= '0' && randomAscii <= '9';
boolean isUppercase= randomAscii >= 'A' && randomAscii <= 'Z';
boolean isLowercase= randomAscii >= 'a' && randomAscii <= 'z';
if (isDigit|| isUppercase || isLowercase) {
randomChars[count] = (char) (randomAscii);
++count;
}
}
return new String(randomChars);
}
}
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
上次更新: 2020/08/24, 10:08:00