什么是整洁的代码
C++之父 Bjarne Stroustrup 认为:
代码逻辑应该直截了当,叫缺陷难以隐藏;
尽量减少依赖关系,使之便于维护;
依据某种分层战略完善错误处理代码;
性能调至最优,省的引诱别人做没规矩的优化,搞出一堆混乱来。
关键在于:整洁的代码只做好一件事
《面向对象分析与设计》作者 Grady Booch 认为:
整洁的代码简单直接。
整洁的代码如同优美的散文。
整洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直截了当的控制语句。
关键在于:干净利落的抽象
编写整洁的代码
有意义的命名
-
名副其实
变量、函数或类名应该答复了所有的大问题,如果名称需要注释来补充,那就不算名副其实。
如下
flag变量名,非常不符合名副其实这条原则const flag = true // 是否是原始值 -
避免误导
避免使用与本意相悖的词。
如下
productList变量,名称看起来是一个集合,但值却是字符串,与本意相悖const productList = '255,26,223' -
做有意义的区分
如下三个方法名,很难从名称上区分应该使用哪个
function getProduct() {} function getProducts() {} function getProductList() {} -
使用读得出来的名称
人类善于记忆和使用单词,不要自己造词。
-
使用可搜索的名称
便于搜索,如
MAX_PRICE容易搜索,a不容易找到 避免使用编码
类名和对象名应该是名词或名词短语,不应当是动词
方法名应该是动词或动词短语
-
给每个抽象概念选一个词,并一以贯之
比如获取动作都用
query,queryProductInfo、queryCourseInfo,风格统一,便于理解和阅读 -
别用双关语
避免将同一单词用于不同目的
如下两个
add方法,用于不同目的,难以区分function add(value) { return value + 1 } function add(value) { collection.push(value) } -
添加有意义的语境
如下变量名,
status放在成组的变量名中可以理解是学生考试相关的状态,而单拿出来很难知道具体含义,可以通过添加前缀的方式,给变量添加有意义的语境,如examStatusconst studentName, status, level, score, pass
函数
短小
只做一件事
-
函数参数
- 最理想的参数数量是零,其次是一,二,有足够特殊的理由才能使用三个以上的参数
- 标识参数大声宣布本函数不止做一件事
无副作用
-
分隔指令与询问
function set(attribute, value) { // 设置某个指定属性,成功返回true,不存在返回false } if (set('username', 'xiaoming')) {} // 难以读懂,if语句在问username属性值之前已经设置成xiaoming,还是问username属性成功设置成xiaoming // 解决方案:把指令与询问分隔开 if (attributeExists('username')) { setAttribute('username', 'xiaoming') } 别重复自己
注释
用代码阐述意图 — 与其花时间为糟糕的代码编写注释,不如花时间清理掉糟糕的代码
- 好的注释
- 法律信息,如版权和著作权声明
- 提供信息的注释,如解释方法的返回值
- 对意图的解释
- 阐释
- 警示
- TODO注释
- 坏的注释
- 喃喃自语
- 多余的注释
- 位置标记
- 注释掉的代码
- 信息过多
格式
垂直格式
短文件通常比长文件易于理解。有可能用大多数为200行,最长500行的单个文件构造出出色的系统。
向报纸学习,报纸一般从上到下阅读,在顶部有头条,告诉故事主题,第一段故事大纲,粗线条描述,接着读下去了解所有细节。报纸由多篇文章组成,多数短小精悍,有些稍微长点,很少有占满一整页的。
从报纸可以学习到
- 文件名称应当简单且一目了然,名称本身应该足够告诉我们是否在正确的模块中;
- 源文件顶部应该给出高层次概念和算法,细节应该往下渐次展开,直至找到最底层函数和细节。
具体原则:
-
垂直方向的区隔
几乎所有代码都是从上往下读,从左往右读。每行展现一个表达式或一个句子,每组代码行展示一条完整的思路。这些思路用空白行区隔开来
如下两端代码,同样的代码,垂直方向有区隔的第一段明显比第二段更易阅读
import Vue from 'vue'; import Main from './main.vue'; import merge from 'element-ui/src/utils/merge'; import { PopupManager } from 'element-ui/src/utils/popup'; import { isVNode } from 'element-ui/src/utils/vdom'; const NotificationConstructor = Vue.extend(Main); let instance; let instances = []; let seed = 1; const Notification = function(options) { if (Vue.prototype.$isServer) return; options = merge({}, options); const userOnClose = options.onClose; const id = 'notification_' + seed++; const position = options.position || 'top-right'; options.onClose = function() { Notification.close(id, userOnClose); }; instance = new NotificationConstructor({ data: options }); };import Vue from 'vue'; import Main from './main.vue'; import merge from 'element-ui/src/utils/merge'; import { PopupManager } from 'element-ui/src/utils/popup'; import { isVNode } from 'element-ui/src/utils/vdom'; const NotificationConstructor = Vue.extend(Main); let instance; let instances = []; let seed = 1; const Notification = function(options) { if (Vue.prototype.$isServer) return; options = merge({}, options); const userOnClose = options.onClose; const id = 'notification_' + seed++; const position = options.position || 'top-right'; options.onClose = function() { Notification.close(id, userOnClose); }; instance = new NotificationConstructor({ data: options }); }
-
垂直距离
- 关系密切的概念应该互相靠近。除非有很好的理由,否则不要把关系密切的概念放到不同文件中。
- 变量声明应尽可能靠近其使用位置。短函数中本地变量应该在函数顶部出现,较长函数中变量也可在某个代码块顶部,或在循环之前声明。
- 相关函数应该放在一起。如某个函数调用了另一个,且调用者应该尽可能放在被调用者上面。
- 概念相关代码应该放到一起。相关性越强,彼此之间距离就该越短。
function open(options) {} function doOpen(props) {} function doAfterOpen() {}
-
垂直顺序
一般而言自上向下展开函数调用依赖顺序,这样就建立了一种自顶向下贯穿源代码模块的良好信息流。
横向格式
行宽、间隔、缩进、水平对齐等,每个团队都有各自规范,并通过eslint等格式校验工具约束,不做过多阐述
跃进
Kent Beck 简单设计四条规则
- 运行所有测试—保证系统如预期一般工作
- 不可重复
- 表达了程序员的意图
- 尽可能减少类和方法的数量
只要系统可测试,就会导向保持类短小且目的单一的设计方案。测试编写越多就越能持续走向编写较容易测试的代码,所以,确保系统完全可测试能帮助我们创建更好的设计。
紧耦合的代码难以编写测试。
遵循有关编写测试并持续运行测试的简单、明确的规则,系统就会更贴近低耦合度、高内聚度的目标。编写测试引至更好的设计。
第2-4条都是重构相关工作,重构技巧可以参阅《重构》等书籍。