可复用的代码
这是之前在公司内部做的一次分享,里面没有敏感信息,现在分享出来。
从一个的分支结构开始
看下面的代码:
if (condition1 || condition2 && condition3) {
// do something
}
至少要花5分钟才能确定上面的代码具体在做什么。
看下修改后的代码:
if (condition1) {
// do something
} else if (condition2 && condition3) {
// do something
}
修改后的代码虽然有点笨,但是非常直观。
所以,更推荐写一目了然的代码:
// 推荐
if (condition1 && condition2 && condition3) {
// do something
}
// 推荐
if (condition1 || condition2 || condition3) {
// do something
}
// 不推荐
if (condition1 || condition2 && condition3) {
// do something
}
// 不推荐
return x >= 90 ? "A" : x >= 80 ? "B" : x >= 70 ? "C" : x >= 60 ? "D" : "E";
// 不推荐
return variable != null ? variable.getSomething() : null;
// 推荐
if (variable != null) {
return variable.getSomething();
}
return null;
尽量不使用 break, continue
- 使用 break, continue 会让循环条件变得复杂,难以维护
- 使用 break 或者 continue 往往是对循环逻辑没有考虑清楚
List<String> goodNames = new ArrayList<>();
for (String name: names) {
if (name.contains("bad")) {
continue;
}
goodNames.add(name);
...
}
直接把 continue 的条件反向,就可以避免使用 continue:
List<String> goodNames = new ArrayList<>();
for (String name: names) {
if (!name.contains("bad")) {
goodNames.add(name);
...
}
消除 break 1:
while (condition1) {
...
if (condition2) {
break;
}
}
while (condition1 && !condition2) {
...
}
消除 break 2:
public boolean hasBadName(List<String> names) {
boolean result = false;
for (String name: names) {
if (name.contains("bad")) {
result = true;
break;
}
}
return result;
}
public boolean hasBadName(List<String> names) {
for (String name: names) {
if (name.contains("bad")) {
return true;
}
}
return false;
}
总结一下:
- 把 break 终止条件合并到头部
- 有时使用 break 的地方可以直接 return
总之,写直观的代码可以:
- 减少眼球 parse 代码的时间
- 避免不必要的错误
异常处理
- 不要吞掉任何异常,吞掉异常是掩盖问题
- 异常处理会产生性能开销,所以只包含可能发生异常的代码
- 不要捕获 Exception 这种通用的异常,应该捕获特定的异常
# 不推荐
try:
1 / 0
except Exception as err:
pass
异常处理不要与return,break,continue 混在一起
public static boolean finallyTest() {
try {
int i = 10 / 0;
System.out.println("i vaule is : " + i);
return true;
} catch (Exception e) {
System.out.println(" -- Exception --");
return true; // will be block
} finally {
finallyMethod();
return false; // end
}
}
- 正常情况下 return 会立刻返回
- 但是在异常处理模块,可能会被执行….也可能不会被执行
Golang 中 defer 取代 finally
func main() {
r, err := Open("a")
if err != nil {
log.Fatalf("error opening 'a'\n")
}
defer Close(r)
r, err = Open("b")
if err != nil {
log.Fatalf("error opening 'b'\n")
}
defer Close(r)
}
defer: 后声明的先执行
swift 对异常处理的改进
do {
let tokens = try lexer.lex()
print("Lexer output: \(tokens)")
let parser = Parser(tokens: tokens)
let result = try parser.parse()
print("Parser output: \(result)")
} catch Lexer.Error.invalidCharacter(let character) {
print("Input contained an invalid character: \(character)")
} catch Parser.Error.unexpectedEndOfInput {
print("Unexpected end of input during parsing")
} catch Parser.Error.invalidToken(let token) {
print("Invalid token during parsing: \(token)")
}
catch {
print("An error occurred: \(error)")
}
只针对可能出现问题的语句或者表达式,避免了对整个 block 进行一场捕捉
关于函数
- 一个方法只做一件事情
- 返回值的类型应该是确定的,应该有单一类型的返回值
- 动态语言可以考虑使用 Type Hints,便于检查、重构以及…阅读
function add(x: number, y: number): number {
return x + y;
}
def scale(scalar: float, vector: Vector) -> Vector:
return [scalar * num for num in vector]
函数应该短小
函数参数
关于函数层级
- 算法和业务逻辑应该是分离的
- 应该是自顶向下,不要跳转
日志
- 使用标准的日志输出方法,不要使用 print
- 定义日志的级别,可以根据日志的级别对日志进行 filter
还有,想清楚了再写
- 整体架构
- 流程分析
- 数据流转
- 细节
- 还有,抑制立刻开始编码的冲动…
还有,第一时间 review
- 这个时候,是你思路最清晰的时候
- 这个时候,花的时间最少
- 这个时候,很多显而易见的问题会被发现
以上,就这些内容,希望对你有所帮助!
3 thoughts on “可复用的代码”
If的条件判断应该写得容易阅读没问题。不用在乎代码多几行。
关于break 和 continue。在可以的时候,直接return是对的。不过如果你想完全干掉break和continue,可能做不到。
1. 判断是否break和contiue的条件是有可能在循环执行过程中变化的。
2. 你所谓的干掉continue,是用一个条件判断包裹continue以后的所有代码。如果这段代码比较长呢?如果你有几个continue呢?那就要条件判断里面嵌套条件判断?可能反而不方便阅读理解代码逻辑
如果continue后面的代码比较长,是不是可以提取出来作为一个方法调用?
如果一个地方有大量的continue,if,break 之类的跳转,可能需要考虑从设计上优化这些代码,是否可以采用一些设计模式来简化?
总之,是想办法尽可能降低代码的熵。
你说的也没错。
我想说的是,这个事情没有绝对的。而且,从代码优化的工作量上面来说,也许你需要分成一期工程,二期工程……