本文的主要内容来自经典数据<<程序员修炼之道>>, <<代码整洁之道>>和<<重构–改善既有代码的设计>>, 整理了这些书中我觉得比较重要的, 应该努力达到的要求.
提供各种选择而不要找蹩脚的借口
在接收一个任务后, 就相当于承诺了正确的完成这一任务. 因此需要对任务中可能出现的问题进行预估, 并主动承担相应的责任. 对于可能出现的风险应该提供相应的预案, 而不是在问题出现后声称”自己的代码被猫吃了”.
不要说事情做不到, 而是说明做什么可以挽回局面.
不要容忍破窗户
不要放过代码中存在的”破窗户”(例如低劣的设计, 糟糕的代码), 看到一个就修复一个, 并且要进一步的考虑该问题是否存在共性, 其他地方是否存在同样的错误. 实在无法修复的代码也应该进行一些处理, 防止这些代码扩散.
软件同样具有熵, 如果不经常性的对代码进行维护, 其熵值就会不断的增加, 并最终导致项目质量恶化.
定期为你的知识资产投资
知识和经验是最重要的职业资产. 然而这些资产是具有时效性的, 技术和市场需求的发展都会导致原有的一些知识过时. 个人所掌握的知识的价值降低会导致个人对于公司的价值降低. 因此, 为了避免这一情况, 需要考虑
- 进行定期投资. 不断地学习, 即使每次学习的内容不多, 也要保证不断地学习.
- 多元化. 知道的事情越多, 综合能力越强. 掌握工作中需要的知识只是底线, 需要主动的学习不同的技术, 不同的知识.
- 管理风险. 既要学习风险高回报高的技术, 也要学习风险低回报低的技术. 鸡蛋不放在一个篮子里.
- 低买高卖. 关注新技术, 新技术更有可能获得高回报
- 重新评估和平衡. 周期性的评估学到的知识的价值, 并根据情况作出调整.
基于以上内容, 可以指定一些较为具体的目标, 例如
- 每年学习一种新的语言
- 每季度阅读一本新的技术类书籍
- 阅读非技术类书籍
- 参加课程
- 加入本地用户组, 与其他人交流, 避免与世隔绝.
不要放过问题, 每个问题都是学习的机会
如果遇到了问题, 不要轻易的放过问题. 将找到问题的答案视为一个挑战, 通过查阅资料, 请教其他人等方式寻求答案. 与其他人交流的过程也是人际关系网络建立的过程. 即使无法解决现有问题, 在查阅资料和与其他人交流过程中附带的收获也能使自己的知识资产增加.
做好规划, 让自己在空闲时间总是有东西可以阅读.
交流
规划自己想说的内容, 写成大纲. 反复问自己”这是否讲清楚了我想说的全部内容?”, 直到确实如此为止.
要在脑海里形成听众的画面, 可以遵循WISDOM原则
- What do you want them to learn
- What is their interest in what you’ve got to say
- How sophisticated are they?
- How much detail do they want?
- Whom do you want to own the information
- How can you motivate them to listen to you?
即使是同一件事情, 对于不同的听众, 也应该采取不同的方法进行讲解. 例如有些听众希望简短, 而另一些听众希望详实. 不同领域的听众对于一个问题的关注点也往往不同. 例如市场部门关心产品的优势, 而技术部门关心技术价值.
不要重复自己&保持正交性
每次写代码都要问自己, 有没有产生不必要的重复? 有没有使得系统中的各个功能保持正交?
编写代码应该”羞怯”且”懒惰”, 对开始之前需要接受的东西要尽可能严格, 而允诺返回的东西要尽可能少. 如果一个函数可以接受任何东西并返回任何东西, 那就表明这个函数有大量的代码要写.
可撤销性
不存在最终决策, 需求总是会随着产品的开发发生变化, 任何当前看起来不会发生变化的假设在未来都可能发生变化, 因此要尽量保证代码架构的灵活性与可撤销性.
代码架构上的灵活可以使的模型的替换不影响其他部分, 可撤销性保证决策可以反悔.
估算
估算以避免发生意外. 在开发过程中有很多时候需要对问题进行估算. 估算的精度取决于问题的需要. 估算有两种思路, 第一种是将问题分解, 寻找其中的主要因素并估计主要因素的值. 只要分解过程正确, 则对主要因素的估计越准确, 则结果估计越准确. 第二种是向有类似经历的人请教具体花费的时间, 这往往能相当大程度的参考价值.
可以不断的追踪估计的结果, 分析估计值与实际值的偏差, 找出其中的原因并修复估算模型.
基本工具
知识的最佳存储方式是纯文本. 纯文本可以保证人类可读和机器可读, 因此相较于二进制文件通用性更强. 同时具有一定的自我解释性的纯文本也容易理解和处理. 在此基础上, 应该学习一种对于文本编辑更加合适的语言, 从而能够高效地处理文字.
Shell能通过组合的方式实现复杂的功能, 同时脚本可以将任何成功操作的序列记录并自动执行, 因此相较于GUI操作更能避免重复自己.
用好一种编辑器对于开发效率有很大的提高, 这种编辑器不一定非要是Vim或者Emacs, 只要能解决自己的需求, 就是合适的编辑器.
按照合约设计
在写每一个函数之前, 都应该考虑这个函数的前条件, 后条件和类不变项是什么.
前条件是函数调用前必须为真的条件, 如果其不为真, 则函数一定不能调用. 后条件是调用函数后该函数保证一定为做的事情. 类不变项是函数调用之前和调用之后都应该为真的条件.
要崩溃不要破坏
程序发现问题时应该尽早崩溃, 而不是等错误产生更大的影响后导致程序崩溃.
怎样深思熟虑地编程
- 总是意识到自己在做什么, 不要让事情慢慢失去控制
- 不要盲目地编程, 不要构建自己不理解的应用, 或者使用不熟悉的技术, 不要被巧合误导
- 按照计划行事
- 依靠可靠的事情, 为自己的假定建立文档, 这有助于澄清头脑中的假定并有助于将其传达给其他人
- 不要只测试自己的代码, 也要测试自己的假设. 使用断言判断自己的假设, 如果是对的, 则记录到文档之中, 否则应该庆幸提前发现了一个错误
- 为工作划分优先级, 将时间用在最重要的事情上.
- 不要被历史的代码限制, 项目中的任何代码都可以被重构.
重构
当代码具有如下的特征时, 应该考虑重构代码
- 重复
- 非正交的设计
- 过时的知识
- 性能不足
重构是一项需要慎重, 深思熟虑的活动, 需要注意以下几点
- 不要试图在重构的同时增加新功能
- 开始重构之前, 确保拥有良好的测试, 并尽可能经常性的运行这些测试. 这样可以保证如果发生破坏能够尽快知道.
- 采用短小, 深思熟虑的步骤. 每个步骤保持短小, 并进行测试能够避免出现问题后的长时间调试
无情的测试
大多数开发者都讨厌测试, 往往会下意思的避开代码的脆弱之处. 但与此相反, 测试就应该是发现代码的脆弱之处的. 要早测试, 常测试, 自动测试.
测试代码是否正确也需要测试, 可以通过”蓄意破坏”代码来测试现有的测试代码能否正确的捕捉错误.
测试应该做到一个BUG只抓一次. 发现了一个BUG后就应该加入到测试用例之中, 避免同样的错误再次出现.
变量命名
变量命名是代码整洁的基础, 应该努力保证
名副其实, 变量的名字能够准确的表达变量的含义. 如果发现不名副其实的变量名, 就应该立刻修改. 变量名应该尽量保持准确, 而不要使用过于抽象的名字, 例如theList
做有意义的区分. 不要定义太多类似的变量, 例如Product和ProductData, ProductInfo之间就看不出太多的差异. 一些前缀, 比如a
, the
, 以及一些后缀, 比如Table
, Variable
都是没有意义的单词, 这种信息对于理解变量的含义没有任何的意义.
编写函数
编写函数最重要的思想是保持短小. 一个短小的函数总是比一个长函数更容易理解.
每个函数只做一件事. 如果一个函数不能简单的概括要做什么, 就说明抽象程度不够, 应该进行拆分. 每个函数位于一个抽象层次. 阅读函数的过程中, 应该能有一种为了达到A目的, 首先设置B, 然后执行C; 为了设置B, 首先执行D, 然后执行E
的逻辑顺序.
函数的参数应该越少越好, 最好是没有参数, 其次是1个参数, 再次是2个参数. 3个及以上参数的函数都不便于理解, 出现这种情况时应该对参数封装为合适的结构体. 函数的参数不应该有布尔变量, 这种情况必然违背了一个函数只做一件事的原则.
分割指令和询问. 一个函数要么完成一件事, 要么回答一个询问. 不要让一个函数即完成指令又回答询问, 这依然违背了一个函数只做一件事原则.
写代码就像写论文, 很难一次性写出满足规则的代码, 所以通常都是写写出需要的代码, 然后逐步调整成需要的样子.
代码注释
最好的注释就是不写注释. 如果通过合适的函数命名, 使得代码具有很高的可读性, 则不需要任何注释. 如果注释用于解释代码, 那么就应该考虑是不是代码写的不够清晰.
代码的坏味道
以下是一些常见的代码的坏味道, 出现这些情况的时候, 就应该考虑进行一些重构.
- 无法理解的变量名, 与实际含义不匹配的变量名
- 重复的代码
- 过长的函数. 函数应该保持短小, 通过合适的函数命名使得代码中不需要再进行额外的注释
- 过长的参数列表
- 全局数据和可变数据. 变量作用域越大, 就越应该限制其可变性
- 发散式变化和霰弹式修改. 如果一个修改需要同时修改代码的多个地方, 则说明代码设计存在问题.
- 基本类型偏执. 根据实际问题的需要创建一些数据类型, 避免始终使用基本数据类型. 例如电话号码就应该是一种类型而不是一个字符串.
最后更新: 2023年08月30日 14:44
版权声明:本文为原创文章,转载请注明出处