艺术书并不保证你读过之后能成为艺术家,只能告诉你其他艺术家用过的工具、技术和思维过程。

第一章 烂代码的代价

  • 维护
  • 混乱
  • 生产力下降
  • 第二章 命名

  • 名副其实

    • 如果需要注释补充,那就不算名副其实
  • 一旦有更好的就换掉旧的
  • 命名之间不同之处不要太小
    • 1 l O 0
    • 相近的单词
  • 不要意义相近的废话
    • data info
    • a an the
  • 不要冗余
    • table
    • nameString
    • moneyAmout
  • 用读的出来的名称
    • 更好记忆
    • 也更好讨论
  • 可搜索的名称
    • const 一个常量时的名字
    • 不要单个字母做变量,一搜到处都出现过。这种最好只用于本地变量
    • 名称长短与其作用域大小正比
  • 避免使用编码
    • 多余的标记
    • 多余的前缀
      • IShapeFactory :这点有点不是特别认可,写 TS 的时候写接口时前面加个 I 有时也挺舒服的,不会与其他东西搞混。比如 IButton 就是 Button 的接口,那不然命名成什么?
  • 避免思维映射,明确是王道
  • 类名和方法名
    • 一个是 名词或名词短语
    • 一个应该是动词或动词短语
  • 别扮可爱 这点有点意思,我的理解是大概就是说 不要懂这个梗才懂这个名字的意思
    • 俗语、俚语
  • 每个概念对应一个词
    • fetch、get、restrieve
    • controller、manager
  • 不要双关语
  • 使用解决方案领域的专业术语
    • 谁读你的代码,就是程序员了,程序员都懂的简洁术语,技术性的名字,大部分都靠谱
  • 所设领域的名称
  • 一定的前缀来提供语境

    • state、AddrState
    • 更好的方法是 创建一个 Address 的类,编译器会告诉你,这 state 是什么的 state
    • 因为这种方法很容易 你输入一个A ,IDE给你一个长长的列表,你根本找不到你想要的变量。

      第三章 函数

  • 短小

    • 函数就该小,越小越好
    • if else while 其中的代码最好只有一行,例如一个函数调用语句,并且该函数名也要具有说明性
    • 换句话说 ,也就是缩进不要多余一定的层数
  • 只做一件事,做好一件事
    • 如何判断:该函数是否还能再拆出一个函数
    • 分割指令与询问
      • 要么做什么,要么回答上面。不要混乱
        • if(set('a','b')非常糟糕~
      • 用抛出异常 来代替返回错误码
  • 每个函数一个抽象层级
    • 我的理解是
      • 基础API
      • 封装的细节
  • 自顶向下的阅读顺序
    • 每个函数后面跟着下一抽象层级的函数
    • 我的理解是:
      • A:处理一个字符串
      • B:用处理后的字符串发送请求
      • 那么 B 就在 A 的上面?
  • 参数
    • 参数越少越好,尽量不要三个以上
      • 测试样例的排列组合…
    • 除了输入参数 还有输出参数
      • String transform 和 void transform 肯定是前者好
      • 函数必须改变某个状态时,最好是修改所属对象的状态
    • 标识参数 no~ 传入 Boolean 值来决定参数干什么…
      • 这种还是拆开来把~
    • 多个参数时,他们之间的关系最好是这样的
      • 自然的组合 如点的坐标
      • 自然的排序
      • 不要这样:
        • 如果是一个值的有序组成部分 不要!
        • 如果甚至是一个值无序组成部分,更加不要!
      • 有时也可以简化,
        • 比如 一个参数传入两个参数,第一个参数表示前提,第二个是操作标识,那前提完全可以抽象为一个类! 方法作为类的方法~
    • 有时参数实在是太多~
      • 可以将参数封装为类或者说对象——前提是 这几个参数属于同一个概念,比如点的xy坐标、联系方式中手机号邮箱、、他们属于一类概念中的一部分
        • 我觉得 TS 接口也算是一种方式
  • 名字~
    • 最好与参数互动起来
      • write(name)
  • 无副作用
    • 函数只做一件事,你懂我意思吗
    • 每次调用,传入相同参数时,结果都是一样的就是无副作用~
    • 有副作用的话
      • 出现 bug 可能难以找寻
      • 耦合性也会下降
  • switch
    • 虽然 switch 会比多层 if 简洁,但要写的短小 仍是困难
      • 一个 switch 生来就是要做 n 件事情
      • 但要确保
        • 每个 switch 都在较低的抽象层级
        • 不要重复
        • 不要
          • 出现新的类型就变长
          • 一个里面不止一件事情
          • 每次添加新类型都要修改
          • 类似结构函数可能无法复用
    • 解决方法:
      • 将 switch 语句埋到抽象工厂之下,别让一般人看到
        • 书里例子是 Java 我也不太清除 JS 该咋搞
  • try catch
    • 最好把他们主体部分抽离出来,另外形成函数。
      • 也就是 try catch 块中 专门调用一个处理函数
    • 错误处理也是只做一件事。只处理错误,别干别的了
  • 别重复自己 DRY原则
    • 重复是邪恶的根源
    • 所有的准则都是为了控制和消除重复
  • 结构化编程
    • 每个函数、每个代码块中都只有一个入口和出口
      • 只有一个return
      • 永远不要有 goto
    • 当然 小函数可能不用约束的那么死。
  • 如何写出好函数

    • 想写文章一样,想到什么就先些什么
    • 然后打磨他
    • 写测试覆盖每行代码,后面分割后也保证测试通过

      第四章 注释

      弥补代码表达意图遭遇的失败
      注释会撒谎,存在的时间越久,就离描述的代码越远~
      因为 注释可能没人去坚持维护,甚至代码分割之后,注释并不一定能分割。
      真实只在一处:代码
  • 注释不能美化糟糕的代码

    • 花时间编写解释糟糕代码的注释,不如花时间清洁糟糕的代码
  • 用代码来阐述
    • 并不一定代码少就能看懂,比如有时再封装一下 if(flag1 && flag2)会好很多
    • if(employee.isEligible)
  • 值得写的注释
    • 法律信息 版权 !!
      • 当然,可以直接指向一分标准许可或是外部文档,肯定不要全部放在注释里
    • 提供基本信息,虽然有时利用函数名称就能解决
      • 返回值
      • 函数做了啥
    • 解释意图
      • 给出解决方案建议
      • 我的理解是有点像 code review 时的 comment
    • 阐释
      • 有些参数或者返回值晦涩难懂。但你不能修改它的情况下(比如第三方库),必要的备注还是要的
      • 这种用之前还是要好好考虑一下,是否有更好的方法
    • 警示
      • 比如 kill 函数,没事就别跑!测试样例不要写
    • TODO 注释
      • 解释实现部分为什么无所作为,将来要做什么
        • 提醒删除特性
        • 取个好名字
      • 这些注释要定期来查看,修补
    • 放大
      • 说明某个看似不合理之物的重要性
    • 公共API中的 doc 注释
      • 比如 strorybook 写文档时对应的注释
  • 不值得写的注释
    • 自言自语,写了和没写一样,穿搭了更多无用的信息
    • 简单函数的注释,完全不需要写。
    • 甚至读注释比读代码更费时间
  • 误导性的注释
    • 这就离谱好吧
    • 虽然初衷可嘉,但是写出不够精确的话
  • 太过循规蹈矩的注释
    • 就像上面的 doc 注释,肯定不是所有函数都需要文档的哇
  • 日志式的注释
    • 记录每次修改..现在交给 git 就好了
  • 纯废话,重复、甚至发牢骚 [doge]
  • 位置标记
  • 括号后的注释
    • 如果你发现你想这样做,那么你该做的就是缩短函数
  • 归属和署名
    • 交给 git 啦
  • 注释掉的代码
    • 放那里越放越久,没人敢删
    • 交给 git 就好啦,不会丢的
  • 和描述代码相离很远的注释
  • 太过细节

    第五章 格式

  • 垂直格式

    • 像报纸一样
      • 源文件名字简单一目了然
      • 细节向下渐次展开
      • 多篇短小精悍的,很少占满一整夜的额
    • 垂直方向上的间隔
      • 声明、引入、函数.. 之间空白行隔开
    • 垂直方向上的靠近
      • 相邻的代码行说明了紧密关系
      • 相邻的东西不要太割裂
      • 方便一览无遗
    • 垂直距离
      • 太远的跳转
        • 做什么之前还要看在哪里
      • 变量声明,要尽量靠近使用位置
        • 循环中的控制变量就该在循环语句中声明
        • 实在不行,也要在同一代码块的顶部
      • 相关函数,若一个函数调用了另外一个,就应该尽量将他们放在一起,调用者尽量在被调用者之前
        • 我:但是 TS 中好像必须要把被调用的放前面?
      • 概念相关,相关性越强,之间的距离也就应该越短
    • 垂直顺序
      • 自上向下展示以来顺序,被调用的放到
  • 横向格式
    • 代码行的宽度
      • 我个人习惯 120 个字符
        • 总之不要拖动左右的滚动条
    • 水平方向上的间隔与靠近
      • 空格字符将紧密的东西连接在一起,但是可以分隔开相关性较弱的
        • 各种操作符周围加上空格,达到强调的目的
        • 函数名和左圆括号之间不加 —— 函数与参数密切相关
        • 函数调用的参数之间隔开——强调逗号
        • 运算符
          • 乘法之间不加空格,因为具有较高优先级,加减用空格隔开
          • 但是很多代码格式化工具无视运算及优先级
    • 水平对齐
      • 如有较长的变量声明列表要对齐
        • 不要对齐变量,那没有用
        • 问题应该出在长度上,需要拆分
      • 缩进
        • 程序员从代码行左边查看自己在什么范围内工作
        • 尽量不要违反缩进规则
          • 有时 像if else 等 ,比较短小的语句时,我是可以放到一行代码里,但是到后面我也总是回头重新规定格式
        • 若while 语句为空,(这种情况尽量不要
  • 制定/遵守团队规则
    • 如果在一个团队中工作,那就是团队说了算
    • 一组开发者应该认同一种格式风格,让代码有一以贯之的风格
    • 指定规则 - 编写进 IDE 的代码格式功能
      • 像我们前端就是 类似eslint stylelint 配置文件

第六章 对象和数据结构

  • 数据抽象
    • 隐藏具体实现
      • 例如将变量放上一个函数层
        • JS 中getter setter 之类的抽象结接口,以便用户无需了解数据的实现就能操作数据本体
      • 当然不是这样就完事大吉,要以最好的方式呈现某个对象包含的数据
  • 数据、对象的反对称性
    • 过程式代码(使用数据结构的代码)便于在不改动既有数据结构前提下添加新函数;而难以添加新数据结构,因为必须修改所有函数。
    • 面向对象代码便于在不改动既有函数的前提下添加新类;难以添加新函数,因为必须修改所有类
  • 得墨忒耳律:模块不应了解他所操作对象得内部情形
    • 只和朋友谈话,不与陌生人谈话
      • 不应调用由任何函数返回的对象的方法
    • 火车失事:这种风格被称为火车失事
      • 类连串的调用是丑陋的风格

        这一章感觉前端会比较少涉及,所有我结合实践吸收较少

第七章 错误处理

当错误有可能发生,我们就有责任确保发生错误后代码还能照常工作
前提是,处理错误的代码不能搞乱代码逻辑 —— 要编写出既整洁又强固的代码

  • 抛出异常而不是返回码
    • 这个前面也提到过,抛出异常才不会搞乱原先代码逻辑,不会多出一个检查错误码的步骤
  • 先写 try catch finally
    • 这里头的东西可以这么理解:
      • try:事务代码
      • catch:将程序维持在一个持续状态 —— 接续着前面的 try 中的代码
  • 使用不可控异常
    • 这个属于Java 的知识点,不多赘述
  • 定于常规流程
    • 有些错误不能算是错误,只能算是特例,那就 特例模式来处理就好了
  • 不要有返回 null 值的错误处理
    • 不然下一阶段的代码总是要在前面加一个 if( a != null)
    • 纯纯给自己增加工作量和给调用者添乱
    • 完全可以返回空列表之类的别的 “空东西” 来代替 不知所云的 null
  • 别传递 null
    • 返回 null 已经非常那个了 ,传递 null 更糟糕
    • 除非 API 要求…
    • 大多数编程语言没有能对付由调用者以外传入的 null 值
    • 所以 像我们 TS 就可以限制传入类型~ 非常方便

第八章 边界

我们常常依靠外部代码 比如 npm 包
那么该怎么样将外部代码干净利落地整合进自己的代码?

各种原则

  • 开放闭合原则 OCP
  • 别重复自己 DRY