clean code

16 阅读3分钟

前言

通常我们学习都是从一门语言开始,但是一旦脱离对应的语言及平台,我们所学的知识可能没有任何用武之地。

clean code 是一个通用的技术,除了需要极少量的语言特性。而且这种越底层越通用的技术学会了,能够极大的提升内功,日常迭代过程中的增强我们代码的可维护性,可以帮助我们可以毫不费力的就可以写出质量非常高的代码。

而我们日常做 code review 时,这个也会是一个很大的指导原则。

怎么做?

  1. 命名有意义(参数、方法名、常/变量名),不要直接使用数字,使用常量代替。
  2. 尽量短 (类、函数名、函数体、参数)
  3. dry (意图、结构、代码)
  4. 保持一致性

命名

  1. 精准命名
  2. 不能过于宽泛
  3. 应该描述意图,而非细节
  4. 不要用技术术语命名
  5. 用业务语言写代码,建议团队的语汇库
  6. 常量、变量、类、字面量命名应该使用名词, 函数命名应该使用动词
// 精准命名
processChapter(id) => changeChapterToTranslating(id) => startTranslation(id)

yyyymmdd => currentDate

yyyymmddhhmmss => currentTime

86400000 => MILLISECONDS_PER_DAY

cityZipCodeRegex[1], cityZipCodeRegex[2] => [_, city, zipCode] = address.match(cityZipCodeRegex) || []

// 不要用技术术语命名
bookList / bookArray / bookSet => books

函数

  1. 不能有意图重复,只要意图不重复哪怕一模一样也是可以接受的
  2. 参数小于等于2个,不能使用 boolean 参数。如果超过3个参数,考虑封装成类。
  3. 函数行数小于20行
  4. 有返回值的函数不能有副作用,没有返回值的函数可以要有副作用,且副作用应该成对出现
  5. 保证函数内部的所有事情都处在同一个抽象层级
  6. only try block, expect error is one thing
  7. 保证可测试性
verifyUsername() / verifyPassword()

createElement('div', {...props})

参数

  1. remove flag argument
  2. 动静分离,如果一个参数他们的字段变化频率并不一致时,考虑拆分他们到不同的对象中
  3. 聚沙成塔:如果参数很多时,应该封装成对象。如果些对象还具有某些行为,封装成类
createFile(isTemp) => createFile() + createTempFile()

  1. 职责单一、尽量小(字段少、参数少)
  2. 字段分组 - 和参数的动静分离对应

控制语句

  1. 卫语句: 提前 return
  2. 判断条件重复时可以状态模式/策略模式(可以使用表单法来构造) - 开闭原则的体现
if (xxx) {

} else {

}
=>
if (xxx) return xxx
return xxx
function getPrice(type) {
  switch(type) {
    case .type1: return price * 0.8
    case .type2: return price * 0.7
    case .type3: return price * 0.6
  }
}
function getEpubPrice() {
  switch(type) {
    case .type1: return price * 0.85
    case .type2: return price * 0.75
    case .type3: return price * 0.65
  }
}
=>
interface Type {
  getPrice()
  getEpubPrice()
}
class Type1 implements Type {
  getPrice()
  getEpubPrice()
}
class Type2 implements Type {
  getPrice()
  getEpubPrice()
}
class Type3 implements Type {
  getPrice()
  getEpubPrice()
}

switch(type) {
  case .type1: return new Type1()
  case .type2: return new Type2()
  case .type3: return new Type3()
}
// 或者使用表单法
{type1: new Type1(), type2: new Type2(), type3: new Type3()}[type]

function getPrice() {
  return type.getPrice()
}
function getEpubPrice() {
  return type.getEpubPrice()
}

封装

  1. 避免火车残骸代码(迪米特法则/最少知道原则的体现)
  2. 基本类型偏执(这个可以使用 ts 解决)
  3. 一次性完成变量的初始化
  4. 增加防腐层,主要在于判断是不是有可能被替换。

比如我完成一些功能后将其发送到钉钉,但是这个完全可能被替换,那么这个时候,我们是不能在业务直接调用,而应该增加防腐层。(依赖倒置原则的体现 - 高层不应该依赖低层,二者都应该依赖抽象。抽象不依赖于细节,细节应该依赖于抽象。)

a.b.c.d => a.bb

number => type Money = number

let value = ''
if (xxx) value = xxx
else value = xxx
=>
getValue() {
  if (xxx) return xxx
  return xxx
}

class Test {
  feishuService
  sendBook() {
    try {} catch(error) {
      feishuService.send(error)
    }
  }
}
=>
interface FailureService {
  send(error)
}
class FeishuService implements FailureService {}
class Test {
  failureService: FailureService
  sendBook() {
    try {} catch(error) {
      failureService.send(error)
    }
  }
}

引用

  1. clean-code-javascript
  2. 极客时间《代码之丑》