代码整洁之道 读书笔记¶
统计信息:字数 7762 阅读16分钟
作者马丁
第一张 前言¶
代码重构:类似于机器的日常维护,将组建维护到最新的状态,确保组件的正常运行。写出阅读性很好的代码(不仅仅是代码的功能和架构的实现)。代码重构类似于整理房间。日常功能的暂时增减,最后定期进行重构,整体代码更有逻辑。
懂得已有功能和未来需求,才能在已有代码上做出更好的改进。
破窗理论:如果一个窗户已经破损,很久没有人修复,那么其他的窗户也会破损,甚至有人主动破碎窗户。所以,以后做的事情,现在最好完全处理。later is never.
同理,代码上的小问题也需要处理,解决代码的小问题。
每个函数、每个类、每个组件都关注一个事情,不会被周边其他的细节影响。如果一个对象的功能很多,可以抽取成多个对象进行完成。
要求:能通过所有测试;没有重复代码;完成全部设计理念;尽量少的实体(类、方法、函数)。
规定:修改代码时,让代码比来之前更整洁;
第二章 命名¶
函数和变量名应该尽量具体,根据变量名可以看出变量的含义,变量的数据类型等。变量中不要出现 variable, list 等,可以出现 group, 因为变量已经是一个数组,加入这个就是没用的。
代码 01
public class GuessStaticsMessage {
// 创建类变量
private String number;
private String verb;
private String modifier;
// 表示这个方法的返回值是字符串,如果是 void 表示返回值是空
public String make(char candidate, int count) {
createMessageParts(count);
return String.firmat(
);
}
// 根据不同的数量执行不同的函数
private void createDependentMessageParts(int count) {
if (count == 0) {
thereAreNoLetters();
} else if (count == 1) {
thereIsOneLetter();
} else {
thereAreManyLetters(count);
}
}
// 对应三种不同的处理逻辑,并没有放在上面一个函数内部
private void thereAreManyLetters(int count) {
number = Integer.toString(count);
verb = 'are';
modifier = 's';
}
private void thereIsOneLetter() {
number = '1';
verb = 'is';
modifier = '';
}
private void thereAreNoLetters() {
number = 'no';
verb = 'are';
modifier = 's';
}
}
第三章 函数¶
函数需要短小;最好不超过20行(如果超过20行的函数,需要分析函数的功能是否单一,是否可以拆分成多个函数,是否可以去掉无关功能)
if else if 等避免不必要的换行。一个函数体内部不要三层的if嵌套。
函数应该做好一件事,最好这件事,只做这件事。
函数的参数尽量少(最好是0-1,不要超过3个参数)——同理,组件的props也尽量简单,尽量少。这样使用函数或者复用组件更方便。函数参数较少有益于测试。所以说,向函数中传递布尔值的做法不规范(其他数据类型可以进行强制数据转换,如果使用单一的开关函数控制组件可能效果更好)。这样可以设置两个无参数函数。
如果函数的参数天然就是一对(坐标xy等),那么这样比传入一个对象包含两个属性更好。其他的二参数或者三参数,这样可能造成参数的顺序错误,使用不是很方便。如果是三个参数,那么三个参数哪个是可选参数,哪个参数是什么数据类型等问题就会很麻烦。如果是三个参数,最好封装成一个对象。
函数同时没有副作用。一个函数执行时,不能影响组件内部的其他属性或者函数的执行。
尽量避免使用输出函数。如果函数必须修改某种状态时,最好修改对象的属性,而不是直接输出一个变量(除去工具函数)。
抽离 try-catch 结构。这样函数的主要逻辑和错误捕获会更好分离。
代码 02
// 这只一个 web 测试框架
// htmlUtil.java
public static String renderPageWithSetupsAndTreardowns (
PageData PageData, boolean isSuite
) throws Exception {
boolean isTestPage = pageData.hasAttribute("Test");
if (isTestPage) {
WikiPage testPage = pageData.getWikiPage();
StringBuffer newPageCountent = new StringBuffer();
includeSetupPages(testPage, newPageCountent, isSuite);
newPageCountent.append(pageData, getContent());
includeTeardownPages(testPage, newPageCountent, isSuite);
pageData.setContent(newPageCountent.toString());
}
return pageData.getHtml();
}
代码 03
public void delete(Page page) {
try {
deletePageAndAllReferences(page);
}
catch (Exception error) {
logError(e);
}
}
private void deletePageAndAllReferences(Page oage) throws Exception {
deletePage(page);
registry.deleteRefrence(page.name);
configKeys.deleteKey(page.name.makeKey());
}
private void logError(Exception e) {
logger.log(e.getMessgae());
}
函数开始设计时,可能函数功能和函数参数等和最后的功能不合适。所以代码需要打磨。
代码 04
package fitnesse.html;
import fitnesse.responder.run.SuiteResponder;
import fitnesse.wiki.*;
public class SetupTeardownIncluder {
private PageData PageData;
private boolean isSuite;
private WikiPage testPage;
private StringBuffer testPage;
private PageCrawler PageCrawler;
public static String render(PageData PageData) throw Exception {
return render(PageData, false);
}
public static String render(PageData PageData, boolean isSuite) throws Exception {
return new SetupTeardownIncluder(PageData).render(isSuite);
}
private SetupTeardownIncluder(PageData PageData) {
this.PageData = PageData;
testPage = PageData.getWikiPage();
pageCrawler = testPage.getPageCrawler();
newPageContent = new StringBUffer();
}
private String render(blloean isSuite) throws Exception {
this.isSuite = isSuite;
if (isTestPage())
includeSetupAndTeardownPages();
return pageData.getHtml();
}
private boolean isTestPage() throws Exception {
return pageData.hasAttribute("Test");
}
private void includeSetupAndTeardownPages() throws Exception {
includeSetupPages();
includePageContent();
includeTeardownPages();
updatePageContent();
}
}
个人感想:实际项目中的函数或者组件,可能有超过5个参数,或者超过20个 props,因为不同人开发不同功能时,会反复改动一个函数和组件,同时自己没有精力重构这个组件,所以这就是已有的不好的代码。可能是故意写成别人不便于阅读的函数,可能是历史改动造成的问题。尽量让自己写的函数或组件可读可理解。
第四章 注释¶
注释的作用是解释写的不好的代码(这部分代码并不能通过函数名或者变量名看出其中的作用)所以需要必要的注释。(个人学习的注释会说明下参数的类型和变量)对于JAVA等类型明确的语言可以少用注释,对于JS等数据转换很灵活的语言尽量少写注释,减少不必要的注释。
函数的功能会更改,但是注释不一定及时更改,那么注释不一定会准确描述函数的作用。
个人感想:精致的代码不需要注释(实际上代码写的可能有问题,最好还是加上说明,便于自己使用,便于整体团队阅读等)个人和团队的英文能力并不强,所以变量名和函数名不一定代表函数的意思。总之:简单的函数不需要加注释,复杂的函数,特殊情况的处理,需要加注释和引用链接(Stack Overflow)。
读到 86 页。