《编写可读代码的艺术》学习笔记
By D.S.Qiu
尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com
许久没有写博客了,最近状态不是很好,每天下班回来就想睡觉,很多次都是直接第二天睡醒了才洗的澡,一直很想坚持写博客,分享自己的一孔之见,每天都觉得浑身不自在,不畅快。
最近项目上线测试了,每天面对杂乱无章的地方就跟没打扫的卧室一样很揪心,然后跟一位同事聊了挺多关于代码规范方面的,也得到了很多建议和指导,后面就给了这本书《编写可读代码的艺术》——很薄的一本书,但却处处击中我的痛点。以前我总是会觉得如果一本书很薄,最多只能蜻蜓点水般列提纲,甚至有点排斥没几页的书。
上大学以来,感觉自己的总是有点心急,很少会沉下心来看书,虽然去年六月末离校也带了很多买的书出来,但至今都没有“关照”过它们。主要是因为自己太浮躁了,总是急于求成,就沉不下心来好好看书。前几天还跟 雷 同学说,我要是每天可以6点下班,我一定买一堆一堆书。当年在学校(尤其是大四)每次和 代B 从图书馆出来都会在图书馆的日子会一天一天的减少,那个时候就已经开始怀念起图书馆安静看书的氛围,我经常很羡慕 雷 同学的工作节奏,晚上可以看看书才是最奢侈的享受。当然我还没有窘迫到连看书的时间都没有,看着这本书实在有点薄(我其实读书很慢),又急于得到一些代码的规范,就花了几天的时间看了这本书,不得不赞到”短小精悍“。
第一部分 表面层次的改进
可读性之旅从“表面层次”的改进开始:选择好的名字、写好的注释以及把代码整洁的写成更好的格式。这部分的话题很重要,因为会影响你代码库中的每行代码。尽管每个改变可能看上去都小,聚集在一起会造成代码库的巨大改进。如果你不想忍受重构的苦难的话,还是乖乖的从小实践起来。
第2章 把信息装到名字里
无论是命名变量、函数还是类,都有很多相同的原则,尝试把名字当做一条小小的注释,选择一个好名字尽可能让它承载更多信息。
选择专业的词
要选择非常专业的词,避免使用“空洞”的词。
例如,“get" 这个词就非常的不专业,在下面的例子:
def GetPage(urf): ...
“get“这个词没有表达出很多信息。可以有很多理解:这个方法是从本地的缓存中得到一个页面,还是从数据库中,或者从互联网中?如果是从互联网中,更专业的名字可以是 FetchPage() 或者 DownloadPage()。
下面是一个 BinaryTree 类的例子:
class BinaryTree{
int Size();
};
你会觉得 Size() 返回什么呢?树的高度,节点数,还是树在内存中所占的空间?问题是 Size() 没有承载更多的信息,可以由 Height()、NumNodes()或者 MemoryBytes() 代替。
另外一个例子,假设你有某个 Thread 类:
class Thread{
void Stop();
};
Stop() 这个名字还可以,但还可以有更多专业的名字,例如如果不能恢复了,用 Skill() 会更准确些,或者 Pause() ,如果有其他方法让它 Resume() 。
找到更有表现力的词
下面列出一些表现力更强的单词,依语境采用:
send -> deliver 、 dispatch 、 announce 、 distribute 、 route
find -> search 、 extract 、 locate 、 recover
start -> lanch 、 create 、 begin 、 open
make -> create 、 set up 、 build 、 generate 、 compose 、 add 、 new
避免像 tmp 和 retval 这样泛泛的名字
使用像 tmp 、 retval 和 foo 这样的名字往往是”想不出名字“的托词。与其使用这样空洞的名字,不如挑一个能描述这个实体的值或目的的名字。
例如,下面的 JavaScript 函数使用了 retval:
var euclidean norm = function ( v) {
var retval = 0.0f;
for ( var i =0; i < v.length; i+=1)
retval += v[i] * v[i];
return Math.sqrt(retvale);
};
这里的 retval 除了”一个返回值“之外,就没有包含更多信息。 可以使用 sum_squares 更贴切。
tmp
使用 tmp 最经典的例子就是 交换两个变量的值:
if(right < left){
tmp = right;
right = left;
left = tmp;
}
这种情况下, tmp 这个名字很好,因为这里的唯一目的就是临时存储。
但在下面一个例子中使用 tmp 就是懒惰:
String tmp = user.name();
tmp += " " + user.phone_number();
...
template.set("user_info",tmp);
使用像 user_info 这样的名字就更具有描述性。
在下面情况中, tmp 可以出现在名字中,但只是名字的一部分:
tmp_file = tempfile.NamedTemporaryFile();
...
SaveData(tmp_file,...)
这样就会清醒的发现 tmp 到底是文件,文件名还是写入的数据。
循环迭代器
像 i 、 j 、 iter 和 it 等名字做常用的索引和循环迭代器,尽管很空泛,但是大家一眼就知道它们就是一个迭代器。但有时会有比 i 、 j 、 k更贴切的迭代器命名。例如:
for(int i =0; i < clubs.size(); i++)
for(int j = 0; j < clubs[i].members.size(); j++)
for(int k = 0; k < users.size(); k++)
{
if(clubs[i].members[k] = users[j])
… …
}
在上面的的 if 语句中,members[] 和 users[] 很容易用错了索引,而且其他人看起来也费劲,这种情况下,使用更精确的名字会更有帮助,可以使用( club_i , members_i , user_i )或者更简化一点( ci, mi , ui ),这样就一目了然了。
对于空泛名字的裁定
很多时候,仅仅因为懒惰而滥用它们,养成好习惯分几秒钟想出一个号的名字,你会发现你的“命名能力”很快提升。
用具体的名字代替抽象的名字
在给变量,函数或者其他元素命名时,要把它描述的更具体而不是更抽象。
为名字附带更多信息
一个变量名就像是一个小小的注释,有时候我们可以让它更有帮助些:你有一个变量包含一个十六进制字符串:
string id;// Example: "af84"
如果让读者记住这个 ID 的格式很重要的话,改为 hex_id 就更好不过了。
带单位的值
如果你的变量时一个度量值的话,最好让名字带上它的单位。
例如,下面 JavaScript 代码计算页面加载的时间:
var start = (new Date()).getTime();
var elapsed = (new Date()).getTime() - start;
document.writeln("Load time was: " + elapsed + "second");
上面代码逻辑没有问题,但是 getTime() 返回的是毫秒而不是秒。
通过给变量末尾加上 _ms ,就更加明确,减少不必要的混淆和麻烦:
var start_ms = (new Date()).getTime();
var elapsed_ms = (new Date()).getTime() - start;
document.writeln("Load time was: " + elapsed_ms/1000 + "second");
附带其他的重要属性
给名字不附带额外的信息不局限于单位,只要可以更好的描述变量,函数或者其他命名的内容含义就可以加上。
匈牙利表示法就是一个最典型的例子:
pLast p表示指针
名字应该有多长
要选择一个好名字是,有一个隐含的约束就是名字不能太长。对于长名字的精简就的自己拿主意,下面给出一些指导原则。
在小的作用域里可以使用短的名字
小作用域的标记符的所有信息(类型,初始值,等)都很容易看到,所以可以很短的名字。
输入长名字——不再是个问题
现在很多编辑器都有内置“单词补全”的功能,就可以不因为“不好输入”而避免输入长名字。
首字母缩略词和缩写
为了保持更短的名字,程序员可能会把“BackEndManager”命名为“BEManager”,这种名字会很让人费解,尤其是对新的成员。所以经验原则是:团队的新成员是否能理解这个名字的含义?如果能,那可能就没有问题。例如,使用 doc 代替 document 。
丢掉没用的词
有时把名字的某些单词去掉而不会丢失任何信息。例如,ConvertToString() 就不如 ToString() 更简练而没有丢失任何有用的信息。
利用名字的格式来传递含义
对于下划线、连字符和大小写的使用也可以让名字具有更多信息。例如,类的私有变量加上下划线前缀:
private int _offset;
其他格式规范
根据项目和语言的不同,还可以采用其他一些格式规范使得名字包含更多信息。
总结
本章的主题:把信息装入名字中。读者仅通过名字就可以获得大量信息。
下面是讨论过的几点小提示:
使用专业的单词
避免使用空泛的名字
使用具体的名字来更细致地描述事物
给变量名带上重要的细节
为作用域大的名字采用更长的名字
有目的地使用大小写、下划线等。
第3章 不会误解的名字
本章会关注的另一个话题:小心可能会有歧义的名字
例子:Filter()
假设你在写一段操作数据库结果的代码:
results = Database.all_objects.filter("year <= 2011")
结果包含哪些信息?
年份小于或等于2011的对象?
年份不小于或等于2011的对象?
这里的问题是“ Filter ”是一个二义性的单词。
推荐使用 min 和 max 来表示(包含)极限
命名极限最清楚的方式是在要限制的名字前加上 max_ 或者 min_ 。
推荐使用 first 和 last 来表示包含的范围
推荐使用 begin 和 end 来表示包含/排除范围
给布尔值命名
当给布尔变量或者返回布尔值的函数选择名字时,要确保返回 true 和 false 的意义很明确。
下面是一个危险的例子:
bool read_password = true;
这里我们有两种截然不同的解释:
我们需要读取密码。
我们已经读取密码。
在本例中,最好避免使用 “ read ”这个词,用 need_password 或 user_is_authenticated 这样的名字来代替。
通常来讲,加上像 is 、 has 、 can 或 should 这样的词,就可以把布尔值变得更明确。
例如,SpaceLeft() 函数听上去像是返回一个数字,如果它本意是返回一个布尔值,可能 HasSpaceLeft() 这个名字更好一些。
最好避免使用反义词。例如,不要用
bool disable_ssl = false;
与使用者的期望相匹配
例子:get*()
很多程序员都习惯了以 get 开始做的方法做“轻量级访问器”这样的用法,它只是简单地返回一个内部成员变量。如果违背这个习惯很可能会误导使用者。
以下是一个用 Java 写的例子,请不要这样做:
public class StatisticsCollector{
public void getMean(){
//Iterate through all samples and return total/ num_samples.
}
}
在这个例子中,getMean() 的实现是要遍历所有的数据并计算平均值。如果数据量很大的话,用户如果在循环体中直接调用 getMean() 方法,会有很大的代价!
相反,这个方法应当重名为像 computeMean() 这样的名字。
例子:list::size()
例子:如何权衡多个备选名字
当你选择一个好名字时,可能会同时考虑多个备选方案,一定要盘算一下每个名字的好处,才做出选择。
总结
当要定义一个值的上限或下线时,max_ 和 min_ 是很好的前缀。对于包含的范围,first 和 last 是最好的选择。对于包含/排除范围,begin 和 end 是最好的选择,因为它们最常用。
当为布尔值命名时,使用 is 和 has 这样词来明确表示它是布尔值,避免使用反义的词。
要小心用户对特定词的期望。例如,用户会期望 get() 或者 size() 是轻量的方法。
第4章 审美
确切地说,有三条原则:
使用一致的布局,让读者很快就习惯这种风格。
让相似的代码看上去相似。
把相关的代码行分组,形成代码块。
为什么审美这么重要
阅读让人愉悦的代码让人更容易。
重新安排换行来保持一致和紧凑
用方法来整理不规则的东西
用方法(函数)整理不规则的代码块,不仅让代码更有美感,同时还有其他几个效果:
消除了原来代码中的大量重复,让代码变得更加紧凑。
每个功能模块都变得很直白清晰。
添加新的功能应该会更简单。
需要时使用列对齐
整齐的边和列让读者可轻松地浏览文本。
你应该用列对齐吗?
建立和维护对齐的工作量很大,改动的时候会造成更多的“不同”,需要进行调整。还是建议要试试,它并不像担心的那样费工夫,然后成效还不错。
选一个有意义的顺序,始终一致地使用它
下面有一些想法:
让变量顺序与对应的HTML表单中<input>字段的顺序相匹配。
从“最重要”到“最不重要”排序。
按字母顺序排序。
把声明按块组织起来
不要把所有的方法放到一个巨大的代码块中,应当按逻辑把它们分成组。
把代码分成“段落”
个人风格与一致性
遵守项目的习惯,而不是提倡个人风格,因为我们知道一致性要重要得多。
总结
把代码用一致的、有意义的方式“格式化”,可以让代码更用阅读,并且可以读得更快。
下面是讨论过一些具体技巧:
如果多个代码块做相似的事情,尝试让它们有同样的剪影。
把代码按“列”对齐可以让代码更容易浏览。
选择一个有意义的顺序,并始终用这样的顺序。
用空行来把大代码块分成逻辑上的“段落”。
第5章 该写什么样的注释
什么不需要注释
不要为了注释而注释
有时候,程序员会对没有注释的函数有负罪感,以至于他们把函数的名字和参数用句子的形式重写了一遍:
// Find the Node in the given subtree, with the given name, using the given depth.
Node * FindNodeInSubtree(Node * subtree,string name, int depth);
这种情况属于“没有价值的注释”一类,函数的声明与其注释实际是一样的,对于这样的注释要么删除它,要么改进它。
如果你想要在这里写条注释,它最好也能给出更多的重要细节:
// Find a Node with the given "name" or return NULL.
// If depth <= 0, only 'subtree' is inspected.
// If depth == N, only 'subtree' and N levels below are inspected.
Node * FindNodeInSubtree(Node * subtree,string name, int depth);
不要给不好名字加注释——应该把名字改好
好代码 > 坏代码 + 好注释
记录你的思想
加入“导演评论”
你可以在代码中加入注释来记录你对代码有价值的见解。
为代码中的瑕疵写注释
代码始终在演进,并且在这过程中肯定会有瑕疵,不妨用注释把这些瑕疵记录下来。
标记 通常意义
TODO 还没处理的事情
FIXME 已知的无法运行的代码
HACK 对一个问题不得不采用比较粗糙的解决方案
XXX 危险,这里有重要问题
给常量加注释
有些常量不需要注释,因为它们的名字已经很清楚。但是在我们的经验中,很多常量可以通过加注释得以改进。
站在读者的角度
你需要“未雨绸缪”,预料到人们使用代码时可能会遇到的问题。
“全局观”注释
下面是一个文件级别注释的简单例子:
//这个文件包含一些辅助函数,为我们的文件系统提供了更便利的接口
//它处理了文件权限以及其他基本的细节
不要对于写庞大的正式文档这种想法不知所措,几句静心选择的话比什么都没有更强。
总结性注释
在一个函数内部,给“全局观”注释也是个很不错的主意。
最后的思考——克服“作者心理阻滞
程序员要写注释,克服“作者心理阻滞”。
请注意把注释这件事拆成几个简单的步骤:
1.不管你心理想什么,先写下来
2.读下这段注释,看看没有改进的地方
3.不断改进
总结
什么地方不需要这注释:
能不能代码本身中迅速推断的事实
用来粉饰烂代码——应该把代码改好
你应该记录下来的想法包括:
对于为什么代码写成这样而不是那样的内在理由
代码中的缺陷,使用像 TODO: 或者 XXX: 这样的标记
常量背后的故事,为什么是这个值
站在读者的立场上思考:
预料到代码中哪些部分会让读者说“啊?”,并且给它加上注释
为普通读者意料之外的行为加上注释
在文件/类的级别使用“全局观”注释来解释所有的部分如何一起工作的
用注释来中间代码块,使读者不致迷失在细节中
第6章 写出言简意赅的注释
让注释保持紧凑
避免使用不明确的代词
读者要花更多的工夫来“解读”一个代词。有些情况下,“it”或者“this”到底指代什么是不清楚的。
润色粗糙的句子
让注释更精确的过程总是伴随着让注释更紧凑。
精确地描述函数的行为
假设你刚写了一个函数,它统计了一个文件中的行数:
//Return the number of lines in this file.
int CountLines(string filename){...}
上面的注释很不精确,因为“行”有很多定义的方式。下面列出几种特别的情况:
"" (空文件) —— 0或1行
"hello" —— 0或1行
"hello\n" —— 1或2行
"hello\n world" —— 1或2行
"hello\n\r world\r" —— 2、3或4行
下面的注释对于这种实现方法会更好一些:
//Count how many newlines bytes ('\n') are in the file
int CountLines(string filename){...}
用输入/输出例子来说明特别的情况
下面是一个用来移除部分字符串的通用函数:
//Remove the suffix/prefix of "chars" from the input "src".
String Strip(String scr, String chars) {...}
这句注释简直就是多余的,丝毫没有帮助明确函数的作用:
chars 是整个要移除的子串,还是一组无序的字母?
如果在src的结尾有多个chars会怎样?
下面改进下,就明白多了:
//...
// Example: Strip("abba/a/ba","ab") return "/a/"
String Strip(String scr, String chars) {...}
声明代码的意图
“具名函数参数”的注释
“具名函数参数”就是像 C# Python 这类语言的命名函数参数,让每个参数的意义更加明确,对于 C++ Java 还不支持这样的语法的语言,使用“具名函数参数”注释还是很有意义的:
void Connect(int timeout, bool use_encryption) {...}
// Call the function with commented parameters
Connet(/* timeout_ms = */ 10,/*use_encryption = */ false);
采用信息含量高的词
总结
总之,要把更多的信息装入更小的空间里,下面是一些具体的提示:
避免使用代词
尽量精确描述函数的行为
精心挑选输入/输出的例子
声明代码的高层次意图,而非明显的细节
用嵌入注释解释难以理解的函数参数
用含义丰富的词语
第二部分 简化循环和逻辑
第7章 把控制流变得易读
条件语句中参数的顺序
比较左侧
“被询问”的表达式,它的值倾向于不断变化
比较右侧
用来做比较的表达式,它的值更倾向于常量
if/else 语句块的顺序
首先处理正逻辑而不是负逻辑
先处理简单的情况
先处理有趣的或者是可疑的情况
?: 表达式
只有在简单的情况下使用
避免 do/while 循环
从函数中提前返回
臭名昭著的 goto
最小化嵌套
下面是一个简单的例子:
if(user_result == SUCCESS ){
if( permission_result != SUCCESS){
reply.WriteErrors("error reading permissions");
reply.Done();
return;
}
reply.WriteErrors("");
}else{
reply.WriteErrors(user_result);
}
reply.Done();
上面代码你不得不记住 user_result 和 permission_reult 的值,还要不断地切换 SUCCESS 和 non-SUCCESS 条件。
嵌套是如何积累而成的
一开始上面的代码是很简单的:
if(user_result == SUCCESS){
reply.WriteErrors("");
}else{
reply.WriteErrors(user_result);
}
reply.Done();
后面加入第二个操作就编程上面代码的样子了,改动虽然很合理,但是当其他人遇到这段代码时,所有上下文早已不在了。所以,当你对代码做改动时,从全新的角度审视它,把它作为一个整体来看待。
通过提早返回减少嵌套
现在来改进这段代码:
if(user_result != SUCCESS){
reply.WriteErrors(user_result);
reply.Done();
return;
}
if(permission_result != SUCCESS){
reply.WriteErrors(permission_result);
reply.Done();
return;
}
reply.WriteErrors("");
reply.Done();
减少循环内嵌套
你能理解执行的流程吗
不要让代码使用“幕后”运行的结构(线程、信号量/中断处理程序、异常、函数指针或匿名函数、虚方法)比例太高。
总结
第8章 拆分超长表达式
用做解释的变量
下面一个例子:
if line.split(':')[0].strip() == "root"
改成用一个解释变量:
username = line.split(':')[0].strip()
if username == "root"
总结变量
用一个短很多的名字来代替一大块代码,这就是总结变量。
看下面一段代码:
if(request.user.id == document.owner.id){
//
}
...
if(reques.user.id != document.owner.id){
//
}
表达式 request.user.id == document.owner.id 可以用一总结变量来表达更清楚:
final boolean user_owns_document = (request.user.id == document.owner.id);
if(user_owns_document){
}
if(!user_owns_document)
{
}
使用德摩根定理
滥用短逻辑
拆分巨大的语句
下面是 JavaScript 代码需要一次毒很多东西:
var update_highlight = function(message_num){
if($("#vote_value" + message_num).html() === "Up"){
$("#thumbs_up" + message_num).addClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
}else if($("#vote_value" + message_num).html() === "Down"){
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).addClass("highlighted");
}else {
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
}
}
显而易见,这段代码逻辑很清晰,但是看着太复杂,下面改掉:
var update_highlight = function(message_num){
var thumbs_up = $("#thumbs_up" + message_num);
var thumbs_down = $("#thumbs_down" + message_num);
var vote_value = $("#vote_value" + message_num);
var hi = "highlighted";
if(vote_value.html() === "Up"){
thumbs_up.addClass("hi");
thumbs_down.removeClass("hi");
}else if($("#vote_value" + message_num).html() === "Down"){
thumbs_up.removeClass("hi");
thumbs_down.addClass("hi");
}else {
thumbs_up.removeClass("hi");
thumbs_down.removeClass("hi");
}
}
这样做有很多好处:
避免输入的错误。
缩短了行的宽度,更容易快速阅读。
如果类名字改变了,只需要改变一个地方就行了。
另一个简化表达式的创意方法
下面一个例子,每个表达式中包含了很多东西,这次用 C++ 来写:
void AddStats(const Stats& add_from, Stats* add_to){
add_to->set_total_memory(add_from.total_memory() + add_to -> total_memory());
add_to->set_free_memory(add_from.free_memory() + add_to -> free_memory());
add_to->set_swap_memory(add_from.swap_memory() + add_to -> swap_memory());
}
在 C++ 定义一个宏就可以实现化繁为简:
void AddStats(const Stats& add_from, Stats* add_to){
#define ADD_FIEFLD(field) add_to-> set_##field(add_from.field() + add_to->field())
ADD_FIELD(total_memory);
ADD_FIELD(free_memory);
ADD_FIELD(swap_memory);
#undef ADD_FIELD
}
总结
第9章 变量与可读性
变量越多,就越难全部跟踪他们的动向
变量的作用越到,就需要跟踪它的动向越久
变量改变的越频繁,就越难以跟踪它的当前值
减少变量
没有价值的临时变量
下面一段 python 代码中,考虑 now 这个变量:
now = datetime.datetime.now().
replyoot_message.last_view_time = now
now 是一个值得保留的变量吗?不是,下面是原因:
它没有拆分任何复杂的表达式。
它没有做更多的澄清——表达式 datetime.datetime.now() 已经很清楚了。
它只用了一次,因此他并没有压缩任何冗余代码。
减少中间结果
减少控制流变量
缩小变量的作用域
让你的变量对尽量少的代码行可见。
把定义向下移
只写一次的变量更好
总结
减少变量,即那些妨碍的变量。通过离开处理结果来消除“中间结果”变量。
减少每个变量的作用域,越小越好。
只写一次的变量更好,那些只设置一次值的变量(或者 const 、final 、常量)使得代码更容易理解。
第三部分 重新组织代码
第10章 抽取不相关的子问题
积极地发现并抽取不相关的子逻辑:
看看某个函数或代码块,问问自己:这段代码的高层次的目标是什么?
对于每一行代码,问下:它是为了目标而写的么?
如果足够的行数在解决不相关的子问题,试图抽取代码到独立函数中。
纯工具代码
其他多用途代码
意料之外的好处
创建大量通用代码
通用代码很好,因为“完全地从项目的其他部分中解耦出来”。
项目专有的功能
在理想的情况下,你所抽取的子问题对项目一无所知。就算不是这样,分离子问题仍然会创造奇迹。
简化已有接口
人人都爱提供整洁接口的库——那种参数少,不需要很多设置并且通常只需要话一点功夫就可以使用的库。它让你的代码看起来优雅:简单而又强大。
“你永远都不要安于使用不理想的接口”。
按需重塑接口
过犹不及
抽取子问题过于积极,导致过犹不及,引入太多小韩说对可读性是不利的,因为读者要在执行的路径上跳来跳去,要关注更多东西。
总结
把一般代码和项目专有代码分开。
第11章 一次只做一件事
应该把代码组织得一次只做一件事情。
如何给代码整理碎片,下图演示了这个过程:
第12章 把想法变成代码
学会用自然语言描述你要实现的功能,写出和描述匹配的代码。“如果你不能把一件事解释给你的祖母听的话说明你还没有真正理解它。”——阿尔伯特·爱因斯坦
清楚地描述逻辑
了解函数库是有帮助的
把这个方法应用于更大的问题
用自然语言描述解决方案
递归地使用这种方法
第13章 少写代码
最好读的代码就是没有代码。
质疑和拆分你的需求
有些需求可以被削减成一个简单的问题,只需要较少的代码。
保持小的代码库
删除没有用的代码。
熟悉你周边的库
为什么重用库有这么大的好处
节省时间。
总结
从项目中消除不必要的功能,不要过度设计。
重新考虑需求,解决版本最简单的问题
经常性地通读标准库的整个API,保持对它们的熟悉程度。
第四部分 精选话题
第14章 测试与可读性
使测试易于阅读和维护
测试应当具有可读性,以便其他程序员可以舒服地改变或者增加测试。
让错误消息更具可读性
选择好的测试输入
简化输入值
又简单又能完成工作的测试值更好。
为测试函数命名
总结
第15章 设计并改进“分钟/小时计数器”
总结
小结:
这篇学习笔记跨了两个星期,最近身体状态一直不好,一直坚持不下去,然后六月份每天都坚持去跑步,才得以缓解。本来我是很不屑那种比较薄的书的,感觉会讲不透,但是这本书至少第一部分还是很有收获的,我在代码优雅与艺术的路上还要多多修炼。看了第一部分,我就打算写一篇学习笔记记录下自己的感受——抄下来的内容,还是有击中我的痛处的,虽然文字不多,但是完全从书上一点一点的搬过来,确实挺考验我的耐心,一旦出现疲劳感抗拒积极性的时候,就很容易从负面(至少是另一方面)去重新思考原先确定的论调,然后就很容易坚持不下去,在下决心做事情的时候,一定要把自己意志力和满腔热血都考虑进去,所以有自知之明有时候还是不错的,至少不会弄得整天就想法在耍流氓而一事无成。
当意识自己要去恶补自己代码艺术时,google 了很多代码规范方面的书,有时间一定一一拜读:
《代码大全》
《代码质量》
《代码整洁之道》
《代码之美》
《代码之殇》
《重构-改善既有代码的设计》
《程序员修炼之道 从小工到专家》
如果您对D.S.Qiu有任何建议或意见可以在文章后面评论,或者发邮件(gd.s.qiu@gmail.com)交流,您的鼓励和支持是我前进的动力,希望能有更多更好的分享。
转载请在文首注明出处:http://dsqiu.iteye.com/blog/2077157
更多精彩请关注D.S.Qiu的博客和微博(ID:静水逐风)
相关推荐
### Java学习笔记精要 #### 一、Java技术基础概览 **1.1 编程语言** 在探讨Java之前,我们先理解编程语言的基本分类。编程语言是人与计算机交流的工具,按其功能和特性可分为三类:机器语言、汇编语言以及高级...
《Python之禅》是Python编程语言的核心哲学,由Tim Peters所提出,它为Python程序员提供了一套关于如何编写更优雅、更易于理解和维护的代码的指导原则。以下是对这些原则的详细解读: 1. **优美胜于丑陋**:Python...
通过阅读这些书籍,Java开发者可以系统地学习和提升他们的编程技能,理解面向对象设计的原则,熟悉Web开发框架的运用,并掌握编写高质量代码的艺术。无论是初学者还是经验丰富的开发者,这些资源都能提供宝贵的指导...
学习者应学会编写清晰的函数和类文档,以及在关键代码处添加注释,以方便他人理解和维护。 七、持续学习与实践 编程是一门不断发展的技艺,学习者应保持对新技术的关注,如云计算、大数据、人工智能等。同时,通过...
在提供的压缩包文件中,"C语言原程序俄罗斯方块的编写.txt"可能包含了实际的源代码,其他文本文件可能是辅助文档或笔记。仔细研究这些资源,将有助于你更深入地学习和理解C语言编程和游戏开发的精髓。
“Drake Typora Theme”可能是一种以音乐艺术家Drake为主题的设计,它可能带有独特的色彩搭配和个性化元素,为你的Typora编辑器带来一种与众不同的感觉。 3. **typora-ChocolateBox-theme-main.zip** ...
6. **代码注释和文档**:通过分析提供的代码,学生可能会学习到如何编写清晰的注释和有效的文档,这对于提高代码可读性和维护性至关重要。 7. **调试和测试**:通过项目实践,学生会学习如何定位并修复代码错误,...
在“advanced-creative-coding2021-master”这个压缩包中,包含了整个课程的源代码、笔记、示例项目和其他学习资源。通过对这些材料的深入学习和实践,学生可以系统地掌握高级创意编码的精髓,从而在数字媒体领域中...
ORM将数据库中的表与程序中的类关联起来,通过类的对象来操作数据,避免了直接编写SQL语句,提高了代码的可读性和可维护性。CakePHP ORM正是这一理念的具体实现,它提供了一套丰富的API和功能,包括查询构建器、关系...
Jupyter Notebook,作为一个强大的数据科学和分析平台,通常用于编写代码、展示数据分析结果和创建交互式文档。在网页设计挑战中,Jupyter Notebook可以发挥多种作用: 1. **原型设计**:设计师可以使用Markdown...
通过深入学习Haskell和音乐理论,并熟悉Chordster的源代码和功能,你可以充分利用这个工具创作出富有创意的音乐作品。无论是专业音乐人还是业余爱好者,Chordster都提供了一个独特的平台,将技术与艺术完美融合。
项目的核心是Google Colab,这是一个云端的Jupyter Notebook环境,允许用户编写、运行和共享包含代码、文本和富媒体的文档。Jupyter Notebook是数据科学和机器学习领域广泛使用的交互式工具,它的特点是可读性强,...