《代码大全2》读书笔记
高等级的程序员可能是低等级程序员效率的10~20倍,在编程中数量级的差距是一个经常存在的情况。所以在编程中应该注重能力的提升和积累。不仅仅只是把功能实现、任务完成。
软件开发的隐喻
地心说:以计算机为中心。日心说:以数据库为中心。
实际上再完善一些,个人认为应该是以需求和设计为中心,数据库会围着需求变、后端围着数据库变、前端围着后端变。
但也可以跨层,有些需求不需要依赖后端,可以直接前端实现。有些计算服务不需要存储在数据库中,直接在后端实现。
但一个互联网产品的核心还是需求和设计。
在软件开发隐喻这一节中,有把软件开发比喻成农耕、杜坜养殖。实际上建筑房屋更为贴切。这里面设计到架构、重构、自动模块之类的东西。
谋定而后动:前期准备
只要做几个大项目,就知道很多压力是可以通过提前规划避免的。个人经历是最好导师。
软件开发中最常见的项目封面是糟糕的需求和糟糕的项目规划。大项目要多规划,小项目少规划。
第一次就把事情做好是最合算的。非必要的改动会让你付出昂贵代价。
“稳定的需求是软件开发的圣杯”
在编代码之前,客户无法准确描述自己到底想要什么。开发过程能帮助客户更好的理解他们自己的需求,这是需求变更的主要来源。
在构建期间处理需求变更,确保每个人都知道需求变更的代价,如果需求特别糟糕或者不稳定,并且前很多建议都不可行,就取消这个项目。
关键的构建决策
选择一个技术的时候需要记得判断一下这个技术的浪潮时期,如果是很新的技术,可能文档会写的不太好。同事这个技术本身还有很多没有说明和发现的特性,缺点。可以等个一两年。避免在浪潮早期从事编程。
如果在浪潮后期,可以计划用大部分时间持续稳定的编写新功能。
高质量的代码
好的类的接口如同冰山一角,类的大多数部分都不会暴露在外面
使用信息隐藏的大型程序比不使用信息隐藏的大型程序更容易修改,甚至4倍之多。
设计类和子系统的时候要保持松散的耦合,减少模块之间相互依赖的程度。(P98对各种耦合进行了分类和详细说明)
设计模式可以学一学。但不要滥用,据说有三个阶段(非本书中的内容,CTO说的)
- 看山是山看水是水:只会面向对象
- 看山不是山,看水不是水:学了各种的设计模式,并用过头了
- 看山还是山,看水还是水:最终发现各种设计模式还是来源于面向对象
分治法可以解决大脑中无法容纳一个程序的全部细节的问题。
P109对自上而下和自下而上进行了详细的讨论。
开发时,要倾向于读代码方便而不是写代码方便。(之前可能因为怎么写着方便怎么来,忘了好好设计类的API了,总是发现要用什么就写一个类的方法,发现重复就提取一下,导致可能很快就忘了当时是怎么写的了)
类
如果一个类的包含的属性超过7个,就要考虑是否应该分解成多个小类。如果属性是整数和字符串这样的基本数据类型,7±2可以往高了算,如果是复杂对象,往低了算
回想之前做的炮塔战争的子弹类,子弹的特性实在是太多了,好像就属性就至少有15~20个左右了,既能爆炸,又能分裂,还能穿透,还有加速度、变色、跟踪、等等……现在感觉可以设计成多继承,整一些Mixin类,每个特性做成一个单独的Mixin类,如果是火箭炮子弹,就继承爆炸特性和加速度特性,如果是火焰,就继承膨胀、穿透、变色三种特性
有时候需要对只有一个实例的类表示怀疑态度,这个时候就可以想想单例模式,如果是typescript、可以设计成一个全是static 属性和方法的类。
避免过深的继承树,不要超过6层、但书中的作者认为不要超过2~3层。当要继承的时候确保自己真的是为了避免重复代码来写的。
对继承应该保持警惕的态度,不必要不用继承,实际上还有其他的类与类之间的关系:
如果类之间有共同的属性没有共同的方法,不一定要继承,可以创建一个共同包含这些属性的对象,然后两个类都引用上。
如果方法重复,属性不重复,就创建父类,把方法放到父类里。
方法和属性都有重复的,也创建父类
如果希望父类控制接口,就继承;如果希望子类有更大的灵活性,可以独立定义自己的接口和行为,而不受父类的限制,那么可以使用包含关系。
通常情况下,尽量减少一个类与其他类的协作程度。
P166写了很多子程序内聚性从好到不好的排序
防御式编程
防御式编程其实是:输入验证、参数检查、数据校验、错误处理、容错机制、日志记录……
对于健壮性要求很高的代码,应该先要用断言、再处理错误
异常处理应该局部最小化。
进攻式编程:最好的防御就是大胆进攻,在开发过程中惨败,使人不至于在生产环境中败的太惨。
可以尝试测试优先开发,提前编写测试用例。
伪代码编程(PPP)
伪代码编程是一个不错的东西,注意:要在足够低的层级上写伪代码,不要掩盖实际写代码过程中的细节问题,但要在意图层上写代码。不要用过于实际的编程语言写伪代码
变量
保持变量作用域尽可能的短小。——rutubet很早之前和我说的,现在在这本书上发现了。
什么时候需要建立命名规范?合作、交接、评审、项目大、周期长的时候。
不要让变量有太多特殊复杂的含义、定义,比如正整数代表某个物品的数量,-1代表一种异常情况,-100~-200代表另一种异常情况。等等,个人感觉这些异常情况可以写枚举了,或者单独写一个变量。
变量名、类名需要仔细斟酌,不要随便起,这时候我发现这方面 tailwindcss 是挺好的。
P264~268对变量名的起名方法进行了详细的指导和建议,我觉得这是99%的学校课堂上都不会教的东西。
有时候可以这样命名:scoreTotal、scoreAverage、scoreMax
把修饰词放在名字后面。尤其是有很多种修辞的变量名的时候,能保证开头一致性,看起来清爽。
有时候布尔变量尽量不要用flag,这个好像是很多老C语言教程经常教的。应该用 isStudentFind,等等的详细的命名。
相比写代码的人,名称的意义对于读代码的人更为重要。
项目中避免使用:file1, file2, num1, num2 这样的命名,以及特别容易拼错的单词。
语句章节感觉没啥收获,比较初级
但是表驱动法在前言里看到了,做游戏项目的时候可以用于创建一个excl表,把各种游戏数据填在表里,然后写个程序读取这些数据。很方便。
代码改进
外部质量是对用户的,内部质量是对开发者的。用户不关心内部质量,就是代码写的怎么样。
P472写了关注正确性、易用性、效率、可靠性之间的相互关系表格,太详细了,详细的让我产生怀疑这个是没做详细的调研而编的。
结对编程:就是两个人在一块编程,之前好像看到过表情包还是梗图来着,俩人坐一块,头贴着头面对一个电脑。vscode里也可以实现联机编程,只不过两个人同时写一个文件的时候可能会出现同步问题。
书中提到结对编程的好处,可以两个人共同看一份代码,这样出问题的时候两个人都能维护。效果也更好。
但实际上,线上的结对编程,在vscode里体验的联机编程并没有那么的好,因为很多时候别人是不动的,他可能在用浏览器查一些别的东西,这个时候另一个人是不知道对方在干啥,只是发现对方不动弹了。
代码调优策略
对于python项目而言,可以用C++实现热点区域,和性能要求高的地方。实现python、C++联和编程。但这要求一定要对C/C++熟悉。
P606给了一个语句相对性能表,挺详细。
优化是要实测的,很多时候编译器会对代码进行我们想象不到的优化。我们误以为的优化经验可能经过编译器的更新就不再适用了。
P618,C++代码示例,用查询表代替复杂逻辑,这个写法之前从来没想到过,现在看来还挺妙的。
开根号的计算效率是普通加减法,比较操作慢50倍。如果可以通过代数恒等式简化逻辑,就可以去掉根号。
系统优化考虑
随着项目主键增大,编写代码的时间是线性增长的,但是其他活动例如沟通规划、架构、集成、测试、文档等等的时间是非线性猛增的。
大项目文档是很重要的。
许多程序员会对口头上的“干得好”的表扬产生厌恶,尤其是它来自非技术岗的经理的时候。
缺乏良好的变更控制会浪费时间。
软件开发不同于搬砖,不是做的人越多,完成的工作就越多,因为新人需要花费不少时间来熟悉业务理解代码。但如果项目可以拆分,分配给不同的人做一小部分,那还好。
向上管理
有技术经验但是落后于时代10年的管理者比比皆是。需要告诉管理者这样做,不那样做,诀窍就是要让管理者以为他还是老大。考虑关注管理者的兴趣,按照他们真正想要的方式做事,避免让他们在工作中过多关注不必要的细节,以便他们能够更专注于整体目标和战略方向,想象成对个人工作的一种“封装”
向上管理也可以视为一种“封装”,我们需要将工作呈现给管理者时,应该将其“封装”成他们感兴趣的、易于理解的形式,避免过多地让他们关注细枝末节,而是更专注于大局和关键问题。
或者考虑换一份工作。
集成
集成是把多个零散的小系统,小项目,组合成一个大系统。
这一章讲的太详细了,讲的太多集成的顺序方式。
自文档代码
关于注释还是不注释,在P786有一个辩论。可以看着玩
注释不应该用中文翻译一下英文的函数名之类的,这其实是冗余信息。实际上熟练的程序员对英文的敏感度还是很高的。不需要看中文翻译就知道这个函数名是在干啥。回顾我之前写的很多注释都是这样。应该把这个函数看成一个黑盒,描述参数信息、返回值信息、条件、副作用之类的东西
可以在注释里记录惊喜,指名这样写性能提升了多少。
P798~P805 有详细的注释种类和解释,这个只是冰山一角。
关于软件匠艺
降低复杂度是软件开发的核心,编写程序时,先考虑人再考虑机器。
警惕执念,不要教条主义。
最后,更多信息来源部分还有更多的书籍推荐。
总的来说,这本书收获很大,相当于之前看的技术书,相当于5~10本书的收货量。也有原因是这本书本身就很厚,像个大词典一样。