编程代码书籍推荐(改善代码质量的101个方法读书笔记)
编程理论:三个思想、六个原则
是什么
指导编程的思想。在编程的过程中,人们最重视的莫过于编写出高质量的代码。
高质量的代码是指拥有多种扩种的方法、不存在多余的要素、可读性高,易于理解的代码。
编程中有一套理论专门用来指导人们实现这种高质量的代码。该理论由以下三个思想作为支撑
- 交流
- 简洁
- 灵活性
在追求高质量代码的过程中,这些思想左右着我们的每一个决定。
为什么
编程在不同的问题领域有不同的技术和模式。
虽然不能否认理解和掌握技术的重要性,但单纯的学习只能帮助我们了解技术的表面,并不能帮助我们真正学会使用这项技术。
编程中解决问题的方式是就事论事。由于每次出现的问题都不相同,所以如果不先找出问题所在,我们就无法选择合适的技术来使用。要熟练掌握一项技术,就得明白为什么要使用这项技术,这项技术有什么价值,以及我们应该在什么时候使用它。
此时就需要用到编程理论了,编程理论所展示的思想就是使用各项技术的理由。
怎么做
我们要把编程理论展示的思想用作判断基准。这样做可以更加准确地判断技术和方法适用于否。在把这些思想应用于代码的过程中,我们可能会发现一些新的方法。
不过,把思想直接应用于实际编程未免有些抽象,所以我们需要一个“桥梁”来连接思想与编程。这里有六个原则可以充当两者之间的桥梁。
- 效应局部化
- 重复最少化
- 逻辑与数据一体化
- 对称性
- 声明式表达
- 变动率
关于这三个思想和六个原则,将会在后面文章进行解释。
软件架构基本技法是什么
软件架构基本技法共有以下10种:
- 抽象
- 封装
- 信息隐藏
- 打包
- 关注点分离
- 充足性、完整性、原始性
- 策略和实现的分开
- 接口与实现的分离
- 单一引用点
- 分治
为什么
开发优质的软件需要基本的技法作为支撑。程序员认识到,对于一个问题,特定的解决方案要优于其他方案,于是这些解决方案被重复使用。这些方案就是基本技法。
怎么做
我们要把基本技法应用到代码中。
基本技法不是从某种软件开发技术中总结出来的,这些技法是更为本质的东西,他们适用于一切开发方法以及编程语言。
名字很重要无名,天地之始。有名,万物之母。
翻译:“无名”,可以称为天地的本源。“有名”可以称为万物的开始。
老子《道德经》
是什么
在编程中,我们要将命名视为最重要的课题,谨慎对待。
“命名”这一行为和它的产物“名字”都具有非常重要的价值。
命名
取了一个合适的名字意味着元素被正确理解并被正确地设计了出来。相反,如果取的名字不合适,就证明程序员还没有充分理解该元素的作用
名字本身
程序员之间通过代码进行交流时,名字传递的信息是最多的。
写代码的人和读代码的人很少能站在一起实时进行对话。程序员之间大多通过代码进行交流,一旦名字不够贴切,代码上的沟通就会出现障碍。
为了让这种非实时对话能够顺畅进行,我们必须最大限度地在名字上下功夫。
为什么
名字是面向代码阅读者的“用户界面”。各元素都有适当名字的代码能有效传达其意图,让人充分理解某个东西是怎么做出来的。
以给函数命名为例,名字恰当易懂的函数能通过名字表达其职责,这有助于隐藏函数的内部处理。从结果来看,有以下好处。
在阅读代码时,只看函数名就能大致掌握其内部处理,因此可以跳着阅读内部代码。
在编写代码时,函数名能帮我们理解该函数的目的以及用法,使函数调用变得简单。对编写完的代码来说,恰当的名字起到了说明作用,使代码的可读性大大提高。
程序员并不是因为想读代码才去读的,在充分理解代码之后对其进行修改或添加功能才是他们真正的目的。在阅读代码时,混乱的名字会占用所有的脑部资源,妨碍原本应该进行的作业,使程序员无法着手处理问题。
琢磨名字需要花费很多精力,要想取一个恰当的名字,我们得有强大的思考能力,还要舍得花时间。相反,随便取一个名字不仅不会影响函数的运行,还能节约时间。
不恰当的名字会让代码“负债”,此后,只要代码还在使用,读代码的人、用代码的人都将会受到负面影响。
怎么做
编程要先从名字入手,先给代码中会出现的各个元素取一个能够准确表达意图的名字。
在编写代码的过程中也要时常站在代码使用者和阅读者的角度命名。具体来说,注意以下几点:
名字中尽量多包含信息。将名字视为简短的注释有助于将必要的信息添加进去,可以多准备几个名字,从中选择最合适的一个,这样能进一步提高名字的质量。
名字不能有歧义。命名之后,多问自己几遍这个名字是否有歧义
名字说明的是效果和目的,而不是手段。
可以通过先写测试程序后,后写处理的方式检查一下自己取的名字是否合适。
名字要能念出来,除了便于现实对话之外,还能减轻阅读代码时大脑的负担
名字要能搜索出来,名字如果是一个字母或一个数字,搜索时就会产生无数个结果,给代码的解析带来额外的负担。
KISS原则KISS原则
英文:Keep It Simple, Stupid 或 Keep It Short and Simple
中文:保持代码简洁
简述
编写代码时,要优先保证代码的简洁性。
不管是从0开始编写代码,还是修复故障或扩展功能,都要保持代码简洁。
怎么做
不要画蛇添足。将简洁视为编程的指南针,尽可能将多余的、过剩的要素从代码中删除。
在编程过程中我们要经常问自己程序运行必不可少的要素有哪些。一旦放松警戒,代码就会变得复杂。比如以下几种情况
试图使用新学会的技术
学会一门新技术后,人们倾向于使用新技术写出一些无所谓的代码。但是,代码并不是用来炫耀的,它的作用是给用户提供价值,我们不能在代码上耍聪明。
以备将来之需
有时人们觉得将来会用到某些功能,认为最好趁现在写下来,于是编写过剩的代码。现在用不到的东西,就不应该现在写,因为大多数情况下,这些东西将来也用不到。
擅自增加需求
程序员有时会擅自增加需求,添加多余的代码。他们觉得,某个需求必要与否、正确与否,与其找用户确认,不如自己直接写出来。但是,需求是由用户决定的,程序员不可以擅自增加。
KISS原则适用范围
在软件开发中,KISS原则不仅适用于代码,还适用于功能设计。
功能繁多的复杂软件不会受到用户青睐,功能简洁、接口简洁的软件才会受欢迎。
OCP原则——开闭原则OCP
英文:Open-Closed Principle
中文:开闭原则
是什么
我们要让代码同时满足对扩展开放、对修改关闭这两个属性。
对扩展开放表示代码的行为可以扩展。
对修改关闭表示当对代码的行为进行扩展时,其他代码完全不受到影响。
代码如果同时满足这两个属性,就可以在不影响既有代码的前提下扩展功能。
为什么
不论什么软件,只要它还在生命周期内,就一定会发生变化。而且软件的寿命远比我们想象的要长。因此,我们设计的软件要既能适应变化,又能保持长期的稳定。
这就要求代码能够灵活应对变化,对扩展开放,对修改关闭,如果能满足上述要求,就算需求发生变化,我们只要给代码添加新的行为,就能毫无风险的完成对软件的修改。
怎么做
我们要给代码添加接口。
在设计具有某项功能的模块时,如果让模块的使用者客户端直接调用模块的提供者服务器,我们就可以说这个设计是死板的,因为在这种情况下,如果想使用其他服务器,还需要修改客户端。
因此,我们在客户端和服务器之间为模块的使用者设置“客户端接口”,这个客户端接口由服务器实现。
这样一来,在添加拥有新功能的服务器时,只要该服务器上有客户端接口,客户端就可以直接调用新服务器,我们就不用再修改代码了,也就是说,我们可以在不修改当前代码的前提下添加新功能。
DRY原则——不要重复DRY
英文:Dont Repeat Yourself
中文:不要重复
是什么
不可以重复写相同的代码。将整个逻辑代码复制粘贴到其他地方去是造成代码重复的主要原因。
为什么
代码一旦出现重复,故障修复、添加功能等,代码的改善措施就会变得难以实施。具体来说我们会遇到以下困难。
代码可读性降低
相同的代码出现多次,从量的角度来看是“代码量变大”,从质的角度来看是“复杂度变高”,显然代码可读性会降低。
代码难以修改
当相同代码出现在多个地方时,只有正确修改每一处代码,才能保证整体的一致性,稍有不慎,修改就会出现遗漏。另外,即使代码完全相同,有时某些地方也不同修改,在这种情况下,我们就需要阅读前后代码,判断这一处是否需要修改。
若当前重复的代码之间存在细微差别,我们就需要更加深入地阅读各个位置的代码。控制语句的条件内容或条件数量只要存在一点点差别,理解的难度就会进一步大增。
没有测试
出现重复的代码大多是遗留代码,也就是说这部分代码没有经过任何测试。
怎么做
我们可以通过对代码抽象化操作来消除重复。
对代码的逻辑执行抽象化操作,其实就是给整个处理命名,将其函数化,模块化。至于数据,则需要起一个名字定义为常量。最后将重复的部分全部置换为抽象后的内容。
抽象化的优点:
减少了代码量,减轻了阅读负担
因逻辑和数据有了名称,所以代码可读性提高。
重复的代码集中到一处,我们只对这一处进行修改即可。
抽象化的部分易于重复使用。
从长远看,避免重复的利大于弊,所以,即便要花时间重构,即便要花时间消除代码不能正常运行的风险,即便操作起来有些麻烦,我们也要消除重复的代码。
适用范围
DRY不仅适用于代码,还适用于有关软件开发一切活动。
比如自动化测试、构建和发布的持续集成。
SLAP原则——单一抽象层次原则SLAP
英文:Single Level of Abstraction Principle
中文:单一抽象层次原则
是什么
在编写代码时,我们要将高级别的抽象化概念和低级别抽象化概念分离。
在分离时不能只有高低两层,我们要根据功能的复杂程度对抽象化概念进行分离,然后统一各层的抽象级别。
我们要根据抽象级别对函数进行分割,并且将同一函数中的代码统一为同一个抽象级别。
统一各抽象级别之后,代码就可以像图书一行供人阅读了。
function 高级(){ //1级目录
中级1();
中级2();
}
function 中级1(){ //2级目录-1
低级1();
低级2();
}
function 低级1(){ //正文内容
//处理
}
function 低级2(){ //正文内容
//处理
}
function 中级2(){ //2级目录-2
低级3();
}
function 低级3(){ //正文内容
//处理
}
为什么
将代码分割成级别统一的函数,能使代码具有概括性和可读性。也就是说,函数一览起到了目录的作用,从而使代码拥有概括性。分割后的函数是小块的代码,这就是提升了代码的可读性。
代码统一之后,抽象度相同的处理都在同一个地方。于是,代码变得更加顺畅,更容易让人理解。
相反,如果读到一半代码的抽象度突然发生了改变,流畅感会戛然而止。
怎么做
我们要将函数结构化。
将处理转换为意图清晰、由抽象化级别一致的多个步骤组成的函数。
将函数结构化之后,各函数的处理将以调用比自己低一个级别的函数为中心,这种由其他函数调用组成的函数称为复合函数。
复合函数要尽量小,另外不要在复合函数中调用不同抽象级别的函数,也就是说,一个函数中不能既有“连接数据库”这种低级处理,又有“执行业务逻辑”这种高级处理。
PIE原则——编程要表达出意图PIE
英文:Program Intently and Expressively
中文:编程要表达出意图
是什么
在写代码时,明确表达意图十分重要,这与写诗、写随笔、写博客和写信是一个道理。
这是因为代码是写给人看的,而不是写给编译器看的。
因此,在写代码的时候要在表达上多花心思,将软件运行方式直接地传送给阅读代码的人。
为什么
代码是我们正确、完整地了解软件运行方式的唯一线索。
软件开发过程中虽然会创建诸多文档,但这些文档并不能帮助我们正确认识软件是如何运行的。
需求定义文档只描述了需要什么东西。
基本设计文档只描述了用什么样的软件来实现需求。
详细设计文档只描述了成品软件是什么样的结构,详细设计文档虽然与代码最接近,但代码是动态变更的,而详细设计文档往往做不到同步,更何况并非每个项目都有详细设计文档。所以说详细设计文档并不是百分百存在且百分百有用的。
到头来我们只能通过阅读代码来掌握软件的运行情况。因此,编写可读性高的代码,用代码表达意图是唯一可取的方法。
怎么做
把提高代码可读性作为第一要务。重视代码的可读性,而不是代码的易写性。
读代码的次数远比写代码的次数多。“读代码的效率”应优先于“写代码的效率”。“读代码的效率”同样优先于“执行代码的效率”,因为如果代码的可读性高,提高代码的执行效率也会变得容易一些。
将代码的可读性放在第一位,就意味不能为了炫耀才华而写一些让人难懂的代码。刚学会新技术时,人们容易落入陷阱,明知学会的新技术用不上还炫耀一下。不能让人读懂的代码不是好代码,只有能够向读者准确表达意图的代码,才是好代码。
YAGNI原则——你不会需要它YAGNI
英文:You Aren't Going to Need It
中文:你不会需要它
是什么
只写所需最低限度的代码。不能以“可能会用到”为动机编写代码,我们要在需要的时候写需要的代码。坚持只写当前需要的代码。
为什么
即使事先写好了一大段代码以应对各种情况,这些代码大多也派不上用场。
编程针对的是特定需求,所以再怎么追求通用性,总有无法满足的情况。考虑代码的扩展性,有时人们会把自己认为有用的东西设计进去。可惜这些预想大多不会成真,不能成真,就意味浪费了时间。
况且将扩展性纳入考虑的范围会进一步使代码变得复杂,无用的代码混在其中,使代码可读性变低,难以维护。
怎么做
比起通用性,我们更重视单纯性。
先把通用性带来的可重复使用的特性和扩展性放到一边,将“能用”放入第一位。
在多种设计方案的选择中,我们重点要看的是设计方案的单纯性,而不是通用性。不要选择标榜通用性的复杂方案,要选择以具体需求为基础的简单方案。即使需求增加,功能需要扩展,简单的代码比通用的复杂代码更容易修改。
YAGNI的适用范围
除代码之外,YAGNI还适用于软件的功能。
丰富的功能来看上去很吸引人,但仅凭预想创建出的“没有必要的”功能不但没有人使用,还会令软件整体使用方法复杂化。
七个设计原理①——简单性原理是什么
简单性原理就是追求简单。
说得极端一点,就是自始至终都是以最简单的逻辑写代码,让编程初学者一眼就能看懂。
因此,在编程时我们要重视的是局部的完整性,而不是复杂的整体关联性。
为什么
软件故障常集中在某一个区域,而这些区域都有一个共同的特点,那就是复杂。编写代码时如果追求简单易懂,代码就很难出现问题。
不过,简单易懂的代码往往给人一种不够专业的感觉。这就是经验老到的程序员喜欢写老练高深的代码的原因。所以我们要有足够的定力来抵挡这种诱惑。
怎么做
努力写出自然的代码。放下高超的技巧,坚持用简单的逻辑编写代码。
既然故障集中在代码复杂的区域,那我们只要让代码简单到让故障无处可藏即可。不要盲目的让代码复杂化,臃肿化,要保证代码简洁。
七个设计原理②——同构原理是什么
同构原理就是力求规范。
同等对待相同的东西,坚持不搞特殊。同等对待,举例来说就是同一个模块管理的数值全部采用同一单位、公有函数的参数个数统一等。
为什么
相同的东西用相同的形式表现能够使不同的东西更加突出。不同的东西往往容易产生BUG。遵循同构原理能让我们更容易嗅出代码的异样,从而找出问题所在。
图表和工业制品在设计上追求平衡之美,在这一点上,同构原理也有着相似之处,统一的代码颇具美感,而美的东西一般更容易让人接受,因此统一的代码有较高的可读性。
怎么做
我们要让代码符合一定的规范。不过,这会与程序员的自我表现欲相冲突。
为了展现自己的实力,有些程序员会无视编程规范,编写独特的代码。可靠与简单是代码不可或缺的性质,但这些程序员常常在无意间让代码变得复杂。
这就把智慧与个性用错了地方。小小的自我满足远不及代码质量重要。所以在编写代码时,务必克制住自己的表现欲,以规范为先。
七个设计原理③——对称原理是什么
对称原理就是讲究形式上的对称,比如有上就有下,有左就有右,有主动就有被动。
也就是说,我们在思考一个处理时,也要想到与之对称的处理。比如有给标志位置1的处理,就要有给标志位0的处理。
为什么
具有对称性的代码能够帮助读代码的人推测后面的代码,提高其理解代码的速度。同时,对称性会给代码带来美感,这样有助于他人理解代码。
此外,设计代码时将对称性纳入考虑范围能防止我们在思考问题时出现遗漏。如果说代码的条件分支是故障的温床,那么对称性就是思考的框架,能有效阻止条件遗漏。
怎么做
在出现“条件”的时候,我们要注意它的“反条件”。每个控制条件都存在与之成对的反条件(与指示条件相反的条件)。要注意条件与反条件的统一,保证控制条件具有统一性。
我们还要考虑到例外情况并极力避免其发生。例外情况的特殊性会破坏对称性,成为故障的温床,特殊情况过多意味着需求没有得到整理。此时应重新审视需求,尽量从代码中剔除例外情况。
命名也要讲究对称性。命名时建议使用set/get,start/stop,begin/end和push/pop等成对的词语。
七个设计原理④——层次原理是什么
层次原理就是在结构上讲究层次。
注意事物的主从关系、前后关系和本末关系等层次关系,整理事物的关联性。
不同层次各司其职,同种处理不跨越多个层次,这一点非常重要。比如执行了获取资源的处理,那么释放资源的处理就要在相同的层次进行。又比如互斥控制的标志位置1和置0的处理要在同一层次进行。
为什么
有明确层次结构的代码能帮助读代码的人抽象理解代码的整体结构,读代码的人可以根据自身需要阅读下一层次的代码,掌握更加详细的信息。
这样一来就可以提高代码的可读性,帮助程序员表达编码意图,降低BUG发生的概率。
怎么做
在编写代码时设计各部分的抽象程度,构建层次结构。保证同一个层次中的所有代码抽象程度相同。
另外,高层次的代码要通过外部视角描述低层次的代码,这样做能让调用低层次代码的高层次代码更加简单易懂。
七个设计原理⑤——线性原理是什么
线性原理就是让处理流程尽量走直线。
一个功能如果可以通过多个功能的线性结合来实现,那它的结构就会非常简单。
反过来,用条件分支控制代码,毫无章法地增加状态数等行为会让代码变得难以理解。我们要避免做出这种行为,提高代码的可读性。
“透明”一词可以用来形容代码有较高的可读性,所以线性原理又称为“透明原理”
为什么
复杂的流程是故障处理的温床。
故障多出现在复杂的条件语句和循环语句中,另外,goto等让流程出现跳跃的语句也是故障的多发地。
如果能让处理由高层次流向低层次,一气呵成,代码的可读性就会大幅提高。与此同时,可维护性也将提高,添加功能等改良工作将变得更加容易。
一般来说,自上而下的处理流程简单明快,易于理解。我们应避开复杂反复的处理流程。
怎么做
尽量减少条件分支的数量,编写能让代码阅读者线性地看完整个处理流程的代码。
为此,我们需要把一些特殊的处理拿到主处理之外。保证处理的统一性,注意处理的流程。记得是不是俯瞰代码整体,检查代码是否存在过于复杂的部分。
另外,对于经过长期维护而变得过于复杂的部分,我们可以考虑对其进行重构。明确且可靠的设计不仅对我们自身有益,还可以给负责维护的人带来方便。
七个设计原理⑥——清晰原理是什么
清晰原理就是注意逻辑的清晰性。
逻辑具有清晰性就代表逻辑能清楚证明自身的正确性。也就是说,我们编写的代码要让人一眼就能判断出没有问题。任何不明确的部分都要附有说明。
保证逻辑的清晰性要"不择手段"。在无法使用代码证明逻辑正确性的情况下,我们也可以通过写注释、附文档或画图等方法来证明。不过,证明逻辑的正确性是一件麻烦的事,时间一长,人们就会懒得用辅助手段去证明,转而编写逻辑清晰的代码了。
为什么
代码免不了被人一遍又一遍地阅读,所以代码必须保持较高的可读性。编写代码时如果追求高可读性,我们就不会采用取巧的方式编写代码,编写出的代码会非常自然。
采用取巧的方式编写的代码除了能让计算机运行以外没有任何意义。代码是给人看的,也就由人来修改的,所以我们必须以人为对象来编写代码。
消除代码的不确定性是对自己的作品负责,这么做也可以为后续负责维护的人提供方便。
怎么做
我们要编写逻辑清晰的代码。
为此,我们应选用直观易懂的逻辑。会给读代码的人带来疑问的部分要么消除,要么加以注释。
另外,我们应使用任何人能立刻理解且不存在歧义的术语。要特别注意变量名等一定不能没有意义。
扩展一
重复使用代码是件好事,但也存在风险。在使用某段代码之前,一定要仔细确认其运行条件和上下文。
扩展二
修复现有代码故障,必须建立在充分理解代码的基础上,否则考虑的情况就会不全面,导致代码退化。我们不能只着眼于发生故障的地方。“代码能运行就好”的思想并不适用于修复现有代码的故障。
我们应当充分理解代码,充分理解故障类型,进而提出假设,然后在此基础上修复故障,进行全面测试,从而保证代码质量。
七个设计原理⑦——安全原理是什么
安全原理就是注意安全性,采用相对安全的方法来对具有不确定性的、模糊的部分进行设计和编码。
说得具体一点,就是在编写代码的时可以将不可能的条件考虑进去。比如即便某个if语句一定成立,我们也要考虑else语句的情况;即便某个case语句一定成立,我们也要考虑default语句的情况;即便某个变量不可能为空,也要检查该变量是否为NULL。
为什么
硬件提供的服务必须保证安全,软件也一样。
设计软件时要考虑各种情况,保证软件在各种情况下都能安全运行。这一做法在持续运营服务和防止数据损坏等方面有着积极的意义。
怎么做
选择相对安全的方法对具有不确定性的部分进行设计。列出所有可能的运行情况,确保软件在每种情况下都能安全运行。理解需求和功能,将各种情况正确分解到代码中,这样能有效提高软件安全运行的概率。
为此,我们也要将不可能的条件视为考察对象,对其进行设计和编程。不过,为了统一标准,我们编写代码前最好规定哪些条件需要写,哪些条件不需要写。
代码的必要条件与充分条件
对代码的实现来说,需求和功能的说明书只能算必要条件。
比如说明书里写了需要添加某项数据。然而,当添加数据的操作使数据库发生错误时,说明书并没有明确告诉我们具体的应对措施,比如继续处理然后重试、中止处理并生成日志、让软件停止运行等。
安全性是满足充分条件必不可少的要素。我们要让代码在满足必要条件的同时准确涵盖各种功能的情况,在与用户保持沟通的过程中,让每个实例都坚实可靠。
UNIX思想是什么
UNIX思想源于UNIX文化。它是大量经验的结晶,是编写优质代码的实用性技术的集合。
UNIX思想并不是真正意义上的方法论,它是UNIX文化孕育出的一系列约定俗成的规则,被人们沿用至今。
UNIX思想可以总结为以下几个原则:
- 模块化原则
- 清晰原则
- 组合原则
- 分离原则
- 简单原则
- 简约原则
- 透明性原则
- 健壮性原则
- 表达性原则
- 最小意外原则
- 沉默原则
- 修复原则
- 经济原则
- 生成原则
- 优化原则
- 多样性原则
- 可扩展性原则
为什么
UNIX拥有强大的生命力。
UNIX之所以如此成功,是因为UNIX程序员在设计初期进行了正确的设计判断。这些设计判断得到了全世界优秀开发者的赞同,并被不断完善,于是有了今天的UNIX。
怎么做
我们将UNIX思想的各个原则作为设计的方针。这些原则都经过了历史的考验。
参考资料:
https://blog.csdn.net/u012069234?t=1
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com