接着
设计两次
这里“设计两次”的意思是无论设计一个类,模块还是功能,在设计的时候仔细思考,除了当前的方案,还有那些其它的选择。在众多设计中比较,列出各自的优缺点,然后选出最佳方案。就是对于设计方案,都有两个或者两个以上的选择。
对于大牛而言,也许设计方案显而易见,于是觉得没有必要在不同方案中做遴选。然而这并不是一个好的习惯,这说明,你没有在处理更困难的问题,问题对于你而言太简单了。这不是一个好的现象,因为上坡路总是很难走。当你面对困难的问题的时候,通过对不同设计方案的学习和思考,你会成长到更高的一个层次。
我的解读:在管理理论上有一个叫,就是“在一个等级制度中,每个人趋向于上升到他所不能胜任的地位”。程序员也面临同样的问题,当你的经验和资历不断的提高,你总会遇到你所不能胜任的问题,这个时候就需要通过不断的学习,提高自己。当然也有可能所处的环境无法给你更具挑战的问题。这个时候你就需要考虑,你的下一站在哪里?
为什么要写注释
困扰程序员的两大世界性难题:
- 别人的代码没有注释
- 别人让我给我的代码写注释
程序员通常有各种理由不写注释:
- 好的代码是自解释的
- 没时间写
- 注释很快就会和代码不一致,造成误解
- 我读的其他人的注释都毫无意义
我的解读:其实开发过软件的工程师都能理解写注释的重要性和意义,这并不需要很多的解释。但是“懒惰”是原罪之一,我就是不想写呀不想写。
关于软件开发的七宗罪,请阅读
注释应当用于描述代码中不易理解的部分
如果你一定要对于显而易见的部分增加注释,那么可能你是按代码行数收取工资吧,当然,注释也是算行数的。
选择命名
给变量,类,模块,文件起名字很难,真的很难。好的命名能使得软件设计更容易理解,差的命名更容易产生Bug。
我就被坑过。还是在某存储公司的时候,负责开发一个软件升级的规则模块,根据不同的规则决定能不能升级。当时我的代码release之后,发现客户不能升级了。于是我们在代码中找Bug,后来发现,原因是我的代码判断“hardware”字段来决定目标硬件类型是否匹配,而应该是另一个和“hardware”命名很像的另一个字段来决定要升级的硬件的类型。更糟糕的是,因为这个字段实在是比真正应该判断的字段看上去更合理,进行代码审查的人都没能看出这个问题。而当时没有测试环境能够实际匹配到这个硬件类型,这个问题也没能在测试环节中发现。
注释先行
在实现过程中,把接口和注释先准备好。
修改现有代码
对于修改代码,同样面临着“战术性编程”和“战略性编程”的挑战,是以最少的修改完成任务,还是以重新设计使得系统更合理的角度进行长线投资,需要仔细思考。
我的解读:随便改一些不相关的代码,你可能会发现Bug神奇的消失了,软件开发需要运气,祈祷有的时候真的管用。
一致性
一致性在软件设计里很重要,包括:
- 命名
- 代码风格
- 接口
- 设计模式
- 常量
可以使用以下的方法来保证一致性:
- 文档
- 利用工具/代码审查来强制
- 入乡随俗
- 不要随便改变命名约定
代码应当显而易见
怎么定义代码是不是显而易见,就是带代码审查的时候,如果有人认为这的代码不是容易理解,那么这个代码应该就是有问题的。也许这个代码对你来说很直观,但是代码不是写给自己看的。应该让团队里的其他成员也能读懂你的代码。
有一些使的代码不易理解的元素:
- 事件驱动模式 - 因为不知道事件流控制的顺序
- 范型 - 也许运行时才知道类型,造成阅读的困难
我的解读:最早曾在一家通信企业做管理软件开发,几年后被要求修改自己多年前写的代码,读了好久,愣是没看懂。
软件开发的趋势
John对软件开发重的一些趋势和问题做了总结:
- 面向对象,对于继承,基于接口的继承要优于基于实现的继承
- 敏捷,敏捷的一个潜在问题是导致“战术性编程”为主导,导致系统的复杂性增加
- 单元测试
- 测试驱动,测试驱动的问题是关注功能,而非找到最佳设计
- 设计模式,设计模式的问题可能导致过度应用
- Getter/Seeting, 这个模式可能是冗余的,也许不如直接暴露成员更简单
为性能做设计
关于如何在复杂性和性能之间的权衡,通常更简单的代码运行的更快。当然很有可能更复杂和晦涩的代码性能更高,例如汇编对比Python。设计的时候需要考虑的是为了获得性能的提升,代价是什么?这样的代价是不是值得?
在为了性能做出修改之前,先进行测量。针对关键路径,找到影响性能的核心单元,做出性能改进的设计。
这本书的核心是关于“复杂性”的,软件无疑是一个非常复杂的领域。对于导致复杂的原因,我觉得John的观点没有问题,但是实际上还有很多更深层的原因。软件开发和人息息相关,离开人来讲纯软件的东西,其实并不复杂,软件开发中引起复杂性的更多原因是更为复杂的人,团队,组织,和组织关系。这并不是对该书的否定,这本书对于程序员来说还是很好的一本书,值得一读。