目录
代码整洁的必要性
为什么要有代码
为什么要写得整洁
整洁的艺术
混乱的代价
整洁的定义
规整端正,符合规范
简洁精炼,便于阅读
什么是整洁的代码
怎样写出整洁的代码
不断改进
不要糊弄
具体做法
有意义的命名
使用函数
注释
勤加练习
积累经验
小技巧
#本文参考书籍《代码整洁之道》
#粗体显示大致内容,下划线标记关键内容
在没有意识到整洁代码的重要性之前(下面是一段错误的代码,请不要浪费精力去阅读):
其实我最初写的代码比这还要不堪,我甚至没有缩进,没有格式化代码,于是乎我为了寻找代码中的bug花费了远超写这段代码的时间和精力,以至于发生破窗效应,我后来直接放弃了这段代码
代码整洁的必要性
为什么要有代码
现如今,代码不再是问题(如ChatGPT的问世)。有人认为,我们正在临近代码的终点,代码不再需要人工编写,机器可以根据需求自动生成代码,我们应当把重点放在模型与需求上,提升语言的抽象程度,期望特定领域的语言数量继续增加,但这些实际上都抛不掉代码。
期望代码消失,无异于期望数学失去规范。这相当于希望创造出某种机器,我们只需要张张嘴,机器就能理解我们的内心,将含糊不清的需求转为完美可执行的程序,精确满足我们的需要。从本质上讲,模糊的需求违背了代码的确定性,影响机器的执行。
好比我们去理发,我们对理发师说出自己的需求,但结果往往不尽人意,这是因为我们没能说出具体的规约,缺乏精确的描述,理发师对客户模糊的描述产生误解,而反观代码则不一样,代码具有确定性,即每条语句都不会产生歧义,让机器明确每一步执行什么操作,创造满足客户含糊不清的需求的机器,存在着模糊与明确的矛盾。
也许你会反驳,高明的理发师甚至不需要客户提出自己的要求,却常常能设计出令人满意的发型,但这点如果体现在机器上,那么意味着机器不再与客户进行交互行为,所有产品都将被格式化、规范化,代价则是失去个性化,就像再高明的理发师也只是常常设计出令客户感觉不错的发型,而不是总是,毕竟人与人之间的审美总有不同。
正因为人与人存在差异,人与人的需求绝不尽相同,因此实现个性化成为刚需。只要涉及个性化,我们提出的要求就务必清晰明确,而代码,呈现了需求的细节,它严谨、精确、规范和详细,便于机器理解和执行。无论语言的抽象程度如何提升,我们创造帮组把需求解析和汇整为正式结构的工具,但我们永远无法抛弃必要的精确性——因此,代码永存!
为什么要写得整洁
整洁的艺术
优秀的程序拥有几大特点,可读性就是其一。写代码就像绘画,一副绝美的绘画作品,给人以视觉上的享受,而优秀的代码不仅让人便于阅读,代码各种高亮颜色加之对问题清晰整洁的解决逻辑,给予读者视觉和思维上的双重震撼。
混乱的代价
我们在写代码时难免会遇到思路阻塞的情况,有的人可能会读他人的代码了解思路,也有的人会从头开始分析自己的代码明确逻辑,但这时我们常常被他人糟糕的代码绊倒前进的脚步,又或是盯着自己所写的混乱的代码无从下手,这时我们才惊奇的发现,读代码的时间远远超过了写代码的时间,于是意识到整洁的意义。
整洁的定义
我认为,整是规整,洁是简洁。
规整端正,符合规范
想要做到规整并不难,只要你熟记规范的格式,合理使用缩进,再根据自己的“整洁感”、“代码感”做出一些个性化的规整。
甚至你无需担心格式的问题,几乎所有IDE都提供了格式化工具。
如:Visual Studio 每写完一个语句(写至分号)都会自动格式化
Code::Blocks单击鼠标右键显示格式化工具
简洁精炼,便于阅读
Bjarne(《C++程序设计》作者)用优雅定义整洁。
优雅:外表或举止令人愉悦的优美和雅观;令人愉悦的精致和简单。
Grady(《面向对象分析与设计》作者)认为整洁就是“干净利落的抽象”。
干净利落:果断决绝,就事论事,没有犹豫和不必要的细节。
什么是整洁的代码
整洁的代码要干净简单,避免复杂臃肿,避免复杂就要尽量保证只做一件事、避免四周细节的干扰和污染(一个函数只执行一个功能,一个语句只包含一个函数……),避免多层嵌套和函数中调用另一个函数,避免臃肿就要减少重复,减少不必要的、无用处的代码。
整洁的代码绝不故作高深,每个人例程都清晰明了、深和己意,每个模块都为下一个模块做好准备,读起来无需花费太大力气。我们在解决问题时常常觉得难以用编程语言去解决,但漂亮的代码让我们觉得这个编程语言仿佛就是为了解决这一问题而量身定制。
好的代码像一本优秀的小说,明确展现出要解决问题的张力,并像小说一样将这种张力推向高潮,让读者发出“啊哈,本当如此!”的感概。
好的代码像一个精美绝伦的艺术品,它已经被打磨到完美无瑕,以至让所有妄想改进它的人在苦思冥想良久后,觉得无论改动哪里都会破坏其精美。
怎样写出整洁的代码
整洁的代码是艺术品,是艺术品就需要我们全神贯注去打磨,用心,才能写出好的代码!
不断改进
首先,没有人(天才不能冠以人的称谓)能一上来就写出完美的代码,就像我们做题、写作文,要先构建答题思路,再由核心展开,逐渐丰满其羽翼。
因此,起初不能写出整洁的代码无需灰心,重要的每次都能进步一点,如改一个好的变量名,拆分一个过长的函数,消除一点点重复代码,清理一个嵌套if语句……不断改进代码直至完美。
书中提到美国童子军军规:让营地比你来时更干净。
不要糊弄
谁都想写出整洁的代码,就像谁都想变得优秀,但我们常常因外界各种阻力而放弃,或许是为了提早提交测试,或许生活中还有其他事情要处理,但,糊弄不如不做,因为混乱的代码只会引发更大的混乱,不利于阅读更不利于维护,混乱代码带给我们的干扰往往远超我们糊弄省下的时间。
具体做法
有意义的命名
无论是函数还是变量,我们都需要为它们命名,既然有这么多命名要做,我们不妨做好它。
1.名副其实
除非是循环等特定情况中,我们常以i,j命名循环计数器变量,其他情况下我们的命名要符合这个变量的含义,如:
int number;
//如果定义一个表示数字的变量,这样定义显然比 int i 好
int date;
//time能表示时间的意识,但如果这个变量的含义是表示时间的消逝(以日计),time就显得模糊了
int timeLapseInDays;
//时间消逝(以日计),注意单词首字母大写以区分(首单词首字母可小写,因为它不影响区分)
2.避免误导
避免使用让人误导的命名,如:
int l,O;
//构思和优化代码就已耗费大量精力,为什么还要浪费浪费精力区分小写l大写O是不是数字1或0呢
int basketballAndChicken;
/*
避免一语双关,不要使用有文化特色的词语或是有其他含义的词语;
这样不仅容易让读者误解,还会显得你很没素质。
*/
3.做有意义的区分
有时候我们可能会遇到一些含义相近的词语,这时候我们需要做出区分:
int animals_01;
int animals_02;
int animals_03;
int animalsMonkey,animalsDog,animalsChicken;
/*
当遇到很多变量都有相同含义时(如都表示动物),用数字区分不如用适当的名称区分
*/
4.添加必要的语境
以家养和野生动物情况为例:
void WildAnimals();
void DomesticAnimals();
/*
命名时添加情境,便于理解上下文和抽象的类
*/
5.使用读的出来的名称
void Appl(int* ele,int ord);//错误,无法读出其名称,更无法理解其含义、功能
void AppendList(int* element,int order);//正确,见名知意,实现向链表中添加元素的功能
/*
总之,还是那句话,不要为了节省时间而糊弄,创造出一些难以理解的词语,它只会给他人和你自己添堵
*/
使用函数
1.一大段代码不如一个函数整洁
void Traversal(int max)
{for(int i = 0;i < max;i++);
}
int main()
{Trabersal(max);//在主函数中显得精炼,且见名知义,遍历到最大值
}
2.一个函数尽量集中做一件事
void Hobbies()
{void Ring();void Jump();void Rap();void Basketball();
}
//爱好中包含四件事,不如归纳为一件事简洁
void Hobbies()
{void Ikun();
}
3.函数中避免调用其他函数
见2图中代码,如果函数涉及具体的操作,我们还需要从一个函数跳到另一个函数读取信息,且如果被调函数出错,所有调用该函数的操作都需要更改。
4.函数中代码块要短小,注意缩进
{ {{;}}
}
//使用缩进可以清晰的区分代码作用域及其层次
5.一个函数对应一个抽象层级
自顶向下读代码:向下规则
比如:要先吃饭就要先做饭,要先做饭就要先买菜,要先买菜就要先出门……
6.函数参数
函数参数越好越好,最好是0,参数涉及太多概念性,而且参数不是实体变量,我们传入参数时往往要在翻译一遍
7.无副作用
你期望函数只做一件事,但它可能还会做其它被藏起来的事,有时他会对类中的变量做出未能预期的改动。有时,它会把变量向函数传递的参数或是系统全局变量。副作用具有破坏性,会导致古怪的时序性耦合以及顺序依赖——(书中原话)
注释
尽管本书作者承认有优秀代码的存在,但他总体上反对注释,理由如图:
我的理解是,我仍可作者对注释的理解:不想代码那样具有准确性。但我仍然会使用注释,因为我无法摆脱注释来使程序清晰易懂,且注释的确给我带来便利,但我应当记住:注释会撒谎,而代码不会。所以,我尽量不在注释中添加个性化的理解,使其脱离代码的本真,但我仍然会使用。
勤加练习
任何事情难以一蹴而就,就像物体无法瞬移,勤加练习,深度思考,多多提问,虚心受教,一点点积累经验,一点点水滴石穿。
积累经验
写整洁代码需要遵循大量的小技巧,贯彻、磨炼我们刻苦习得的“整洁感”,这是关键所在。
小技巧
技巧需要自己总结,也要多向他人学习。
比如:
使用宏:
要在一大堆代码中寻找一个数字,好像很困难,但如果定义一个宏,代码着色会让我们很快找到,如果IDE不支持对宏代码着色,将宏大写,也是一个不错的习惯,因为大写更显眼。
使用宏还有一个好处就是:如果我们的MAX上限设置错了,那么我们就不避在循环中逐个更改,只需要在宏定义处修改正确。
使用TAB键:
TAB键缩进:使代码块作用域有层次感;
TAB键快速重命名:
这时我们按下tab键,代码自动补齐include。
创立规则:
如我习惯:
定义宏名称时全部大写以便于我知道这是一个宏;
命名时,命名中第二个单词往后首字母均大写,变量首单词首字母不大写,函数首单词首字母大写,以区分变量和函数名,当然你也可以用下划线区分。
int ikunMember;//这是变量名
int LinkList();//这是函数名
int element;
int element_Append();//这样区分也不错
最后,是对开头代码的改进(当然未能达到期望的最完美的效果):
题目描述:
代码:
函数:
上述代码仍有很多值得改进的地方:注释太多,函数参数太多,太多都是地址传递可能会修改原值引起麻烦,加和函数设计的不够简洁明了(导致我调试了一段时间)。