Published on

可复用的代码

Authors

这是之前在公司内部做的一次分享,里面没有敏感信息,现在分享出来。


从一个的分支结构开始

看下面的代码:

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

  1. 使用 break, continue 会让循环条件变得复杂,难以维护
  2. 使用 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;
}

总结一下:

  1. 把 break 终止条件合并到头部
  2. 有时使用 break 的地方可以直接 return

总之,写直观的代码可以:

  1. 减少眼球 parse 代码的时间
  2. 避免不必要的错误

异常处理

  1. 不要吞掉任何异常,吞掉异常是掩盖问题
  2. 异常处理会产生性能开销,所以只包含可能发生异常的代码
  3. 不要捕获 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
    }
}
  1. 正常情况下 return 会立刻返回
  2. 但是在异常处理模块,可能会被执行….也可能不会被执行

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 进行一场捕捉

关于函数

  1. 一个方法只做一件事情
  2. 返回值的类型应该是确定的,应该有单一类型的返回值
  3. 动态语言可以考虑使用 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]

函数应该短小 函数短小

函数参数 函数参数

关于函数层级

  1. 算法和业务逻辑应该是分离的
  2. 应该是自顶向下,不要跳转

日志

  1. 使用标准的日志输出方法,不要使用 print
  2. 定义日志的级别,可以根据日志的级别对日志进行 filter

日志级别

还有,想清楚了再写

  1. 整体架构
  2. 流程分析
  3. 数据流转
  4. 细节
  5. 还有,抑制立刻开始编码的冲动…

还有,第一时间 review

  1. 这个时候,是你思路最清晰的时候
  2. 这个时候,花的时间最少
  3. 这个时候,很多显而易见的问题会被发现

以上,就这些内容,希望对你有所帮助!