第一次看这本书还是在大三的时候,当时只看了一半就放弃了。如今工作三年多了,昨天晚上大概花了2个小时的时间看完这本书,发现讲的其实还是挺基础的,有些之前不懂的地方现在也豁然开朗了,相信过几年再回读,可能会有新的收获。此之谓常读常新。
不要容忍破窗户
不要留着“破窗户”(低劣的设计、错误决策、或是糟糕的代码)不修。发现一个就修一个。如果没有足够的时间进行适当的修理,就用木板把它钉起来。或许你可以把出问题的代码放入注释(comment out),或是显示“未实现”消息,或是用虚设的数据(dummy data)加以替代
不要追求完美
不要因为过度修饰和过于求精而毁损完好的程序。继续前进,让你的代码凭着自己的质量站立一会儿。它也许不完美,但不用担心:它不可能完美
想要开发一个 App 或者实现一个功能的时候,先让代码跑起来,允许不完美。后续再快速迭代增加新功能和修复 Bug,效果会比什么都规划好之后再动手更好
批判地分析你读到的和听到的
批判地思考你读到的和听到的。你需要确保你的资产中的知识是准确的
Web搜索引擎把某个页面列在最前面,并不意味着那就是最佳选择;内容供应商可以付钱让自己排在前面。书店在显著位置展示某一本书,也并不意味着那就是一本好书,甚至也不说明那是一本受欢迎的书;它们可能是付了钱才放在那里的
从谷歌或百度搜索到的知识进行思考和验证,确保每个进入脑海的知识都是准确的。一般来说,书籍更加权威
DRY – Don’t Repeat Yourself
代码注释
糟糕的代码才需要许多注释。DRY法则告诉我们,要把低级的知识放在代码中,它属于那里;把注释保留给其他的高级说明。否则,我们就是在重复知识,而每一次改变都意味着既要改变代码,也要改变注释。注释将不可避免地变得过时,而不可信任的注释比完全没有注释更糟
注释应该讨论为何要做某事、它的目的和目标。代码已经说明了它是怎样完成的,所以再为此加上注释是多余的,而且违反了 DRY 原则
Use the Power of Command Shells
Always Use Source Code Control
总是。即使你的团队只有你一个人,你的项目只需一周时间;即使那是“用过就扔”的原型;即使你的工作对象并非源码
学习一种文本操纵语言
至少精通一种
编写能编写代码的代码
我自己写的代码生成器:Jce 生成工具、播放器事件生成工具、多终端配置工具
DBC:按合约设计(Design with Contracts)
前条件:为了调用例程,必须为真的条件
后条件:例程保证会做的事情,例程完成时世界的状态
类不变项:从调用者的角度来说,该条件总是为真。例程内部处理过程中,不变项不一定会保持,但在例程退出的时候,不变项必须为真
1 | /** |
谁负责检查前条件,应该是调用者。比如 sqrt(int)
这个函数,参数不能为负数这个前条件应该由调用者来保证
如果它不可能发生,用断言确保它不会发生
不要用断言代替真正的错误处理。断言检查的是决不应该发生的事情
1 | printf("Enter 'Y' or 'N': "); |
处理用户的异常输入应该属于错误处理,不可用断言检查,因为用户输入异常不是不可能发生的事情
在C++异常机制下配平资源
1 | void doSomething(void) { |
注意我们创建的节点是在两个地方释放的——一次是在例程正常的退出路径上,一次是在异常处理器中。这显然违反了DRY原则,可能会发生维护问题。
一种最简单的解决方法是不使用指针即可,栈对象会自动释放;另一种优雅的解决方法是,使用新定义的对象包装好指针对象,这个其实就是 auto_ptr 的原理
1 | // Wrapper class for Node resources |
C++ 中,为什么对指针 delete 之后还要置空?
- NULL 指针可以防止多次 delete 出错
- 防止指针成为野指针;对 NULL 指针解引用会导致运行时错误(野指针更难定位,因为对野指针解引用不一定会出现崩溃)
迪米特法则(德墨忒耳法则/最少知识原则)
迪米特法则规定,某个对象的任何方法都应该只调用属于以下情形的方法:
1 | class Demeter |
举个例子,以下调用不符合迪米特法则
1 | void showBalance(Account account) |
将抽象放进代码,细节放进元数据
npm 的 package.json、hexo 的主题配置、markdown、 git 的 config 等这些都是以元数据的形式进行配置
常识估算
- 简单循环:O(n),比如查找数组最大值
- 嵌套循环:O(n²),比如冒泡排序
- 二分法:O(lg(n)),比如二分查找、遍历二叉树
- 分而治之:O(nlg(n)),划分其输入,并独立地在两个部分上进行处理,然后再把结果组合起来的算法,比如快速排序、归并排序
- 组合:O(Cⁿ),只要算法考虑事物的排列,其运行时间就可能失控,这是因为排列涉及到阶乘,比如旅行商问题
重构注意
- 不要试图在重构的同时增加功能
- 在开始重构之前,确保你拥有良好的测试
- 采用短小、深思熟虑的步骤,重构往往涉及到进行许多局部改动,继而产生更大规模的改动。如果你使你的步骤保持短小,并且在每个步骤之后进行测试,你将能够避免长时间的调试。