艺术书并不保证你读过之后能成为艺术家,只能告诉你其他艺术家用过的工具、技术和思维过程。
第一章 烂代码的代价
- 维护
- 混乱
- 生产力下降
-
第二章 命名
名副其实
- 如果需要注释补充,那就不算名副其实
- 一旦有更好的就换掉旧的
- 命名之间不同之处不要太小
- 1 l O 0
- 相近的单词
- 不要意义相近的废话
- data info
- a an the
- 不要冗余
- table
- nameString
- moneyAmout
- 用读的出来的名称
- 更好记忆
- 也更好讨论
- 可搜索的名称
- const 一个常量时的名字
- 不要单个字母做变量,一搜到处都出现过。这种最好只用于本地变量
- 名称长短与其作用域大小正比
- 避免使用编码
- 多余的标记
- 多余的前缀
- IShapeFactory :这点有点不是特别认可,写 TS 的时候写接口时前面加个 I 有时也挺舒服的,不会与其他东西搞混。比如 IButton 就是 Button 的接口,那不然命名成什么?
- 避免思维映射,明确是王道
- 类名和方法名
- 一个是 名词或名词短语
- 一个应该是动词或动词短语
- 别扮可爱 这点有点意思,我的理解是大概就是说 不要懂这个梗才懂这个名字的意思
- 俗语、俚语
- 每个概念对应一个词
- fetch、get、restrieve
- controller、manager
- 不要双关语
- 使用解决方案领域的专业术语
- 谁读你的代码,就是程序员了,程序员都懂的简洁术语,技术性的名字,大部分都靠谱
- 所设领域的名称
一定的前缀来提供语境
短小
- 函数就该小,越小越好
- if else while 其中的代码最好只有一行,例如一个函数调用语句,并且该函数名也要具有说明性
- 换句话说 ,也就是缩进不要多余一定的层数
- 只做一件事,做好一件事
- 如何判断:该函数是否还能再拆出一个函数
- 分割指令与询问
- 要么做什么,要么回答上面。不要混乱
if(set('a','b')
非常糟糕~
- 用抛出异常 来代替返回错误码
- 要么做什么,要么回答上面。不要混乱
- 每个函数一个抽象层级
- 我的理解是
- 基础API
- 封装的细节
- 我的理解是
- 自顶向下的阅读顺序
- 每个函数后面跟着下一抽象层级的函数
- 我的理解是:
- A:处理一个字符串
- B:用处理后的字符串发送请求
- 那么 B 就在 A 的上面?
- 参数
- 参数越少越好,尽量不要三个以上
- 测试样例的排列组合…
- 除了输入参数 还有输出参数
- String transform 和 void transform 肯定是前者好
- 函数必须改变某个状态时,最好是修改所属对象的状态
- 标识参数 no~ 传入 Boolean 值来决定参数干什么…
- 这种还是拆开来把~
- 多个参数时,他们之间的关系最好是这样的
- 自然的组合 如点的坐标
- 自然的排序
- 不要这样:
- 如果是一个值的有序组成部分 不要!
- 如果甚至是一个值无序组成部分,更加不要!
- 有时也可以简化,
- 比如 一个参数传入两个参数,第一个参数表示前提,第二个是操作标识,那前提完全可以抽象为一个类! 方法作为类的方法~
- 有时参数实在是太多~
- 可以将参数封装为类或者说对象——前提是 这几个参数属于同一个概念,比如点的xy坐标、联系方式中手机号邮箱、、他们属于一类概念中的一部分
- 我觉得 TS 接口也算是一种方式
- 可以将参数封装为类或者说对象——前提是 这几个参数属于同一个概念,比如点的xy坐标、联系方式中手机号邮箱、、他们属于一类概念中的一部分
- 参数越少越好,尽量不要三个以上
- 名字~
- 最好与参数互动起来
- write(name)
- 最好与参数互动起来
- 无副作用
- 函数只做一件事,你懂我意思吗
- 每次调用,传入相同参数时,结果都是一样的就是无副作用~
- 有副作用的话
- 出现 bug 可能难以找寻
- 耦合性也会下降
- switch
- 虽然 switch 会比多层 if 简洁,但要写的短小 仍是困难
- 一个 switch 生来就是要做 n 件事情
- 但要确保
- 每个 switch 都在较低的抽象层级
- 不要重复
- 不要
- 出现新的类型就变长
- 一个里面不止一件事情
- 每次添加新类型都要修改
- 类似结构函数可能无法复用
- 解决方法:
- 将 switch 语句埋到抽象工厂之下,别让一般人看到
- 书里例子是 Java 我也不太清除 JS 该咋搞
- 将 switch 语句埋到抽象工厂之下,别让一般人看到
- 虽然 switch 会比多层 if 简洁,但要写的短小 仍是困难
- 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 个字符
- 总之不要拖动左右的滚动条
- 我个人习惯 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