http://ohcoder.com/blog/2013/03/08/reading-code-complete/
学习并掌握不止一门语言通常是专业程序员职业生涯中的分水岭。一旦一名程序员意识到编程原则是超越特定语言语法的东西时,通往能够实质地改善编程质量并提高工作效率地知识大门也就向他敞开了
要让语言为你的思想服务,而不要让你的思想服务于语言。
http://ohcoder.com/blog/2013/03/17/reading-code-complete/
学习并掌握不止一门语言通常是专业程序员职业生涯中的分水岭。一旦一名程序员意识到编程原则是超越特定语言语法的东西时,通往能够实质地改善编程质量并提高工作效率地知识大门也就向他敞开了
要让语言为你的思想服务,而不要让你的思想服务于语言。
http://ohcoder.com/blog/2013/03/17/reading-code-complete/
(Page.77)软件的首要技术使命:管理复杂度
书中首先阐述了管理复杂度的重要性,并指出产生高代价、低效率的设计源于下面三种根源:
- 用复杂的方法解决简单的问题;
- 用简单但错误的方法解决复杂的问题;
- 用不恰当的复杂方法解决复杂的问题。
关于这一点,书中描述了两种方法来解决这个问题:
- 把任何人在同一时间需要处理的本质(essential)复杂度的量减到最少;
- 不要让偶然性(accidental)的复杂度无谓地快速增长。
- 最小的复杂度(Minimal complexity)
- 易于维护(Ease of maintenance)
- 松散耦合(loose coupling)
- 可扩展性(extensibility)
- 高扇入(high fan-in)
- 低扇出(low fan-out)
- 可移植性(portability)
- 精简性(leanness)
- 层次性(stratification)
- 标准技术(Standard techniques)
其中,关于最小的复杂度部分,我觉得书中有一句话说的很形象。“(Page.80)如果你的设计方案不能让你在专注于程序的一部分时安心地忽视其他部分地话,这一设计就没有什么作用了。”精简性方面,书中指出“(Page.81)精简性意味着设计出的系统没有多余的部分(Wirth 1995,McConnell 1997)。”除此之外,对于高扇入和低扇出,其实之前貌似重视不够,尤其是低扇出,以后要注意这一点了
书中后面讲到隐藏秘密(信息隐藏)。这其中主要分为了两大类:
- 隐藏复杂度
- 隐藏变化源
老实说,看过这部分之后,回想了下之前写过的代码,我觉得做的很不够,和书中所描述的点相差甚远,真是觉得汗颜。也感觉到设计好的接口是如此之难,能够设计好的接口真不是简单吹吹牛就能做好的,需要多方面的综合考虑才有可能更合理的对接口进行设计。
- 设计模式通过提供现成的抽象来减少复杂度
- 设计模式通过把常见解决方案的细节予以制度化来减少出错
- 设计模式通过提供多种设计方案而带来启发性的价值
- 设计模式通过把设计对话提升到一个更高的层次上来简化交流
(Page.138)关注类的接口所表现出来的抽象,比关注类的内聚性更有助于深入地理解类的设计
对于类进行良好的封装,作者给出了几条建议:
(Page.139)尽可能地限制类和成员的可访问性
关于这条建议,书中引用了Meyers的一句话,“(Page.139)应该采用最严格且可行的访问级别(Meyers 1998,Block 2001)”。意思很明显,就是说,如果你不确定,那么多隐藏通常比少隐藏要好。
(Page.139)不要公开暴露成员数据
(Page.139)避免把私用的实现细节放入类的接口中
(Page.141)不要对类的使用者做出任何假设
关于这一点,我想没什么好解释的,很显然的事情。
(Page.141)避免使用友元类(friend class)
书中说到,(Page.141)一般情况下友元类会破坏封装,因为它让你在同一时刻需要考虑更多的代码量,从而增加了复杂度。
(Page.141)不要因为一个子程序里仅使用公用子程序,就把它归入公开接口
这一点考虑的问题是,如果把某个子程序暴露给外界后,接口所展示的抽象是否还能保持一致。
(Page.141)让阅读代码比编写代码更方便
关于这一点,书中强调还是代码的可读性。时刻要防止接口设计的一致性问题,一旦出现接口定义不规范的问题后,不但开始影响代码的可读性,而且还有可能会出现《程序员修炼之道》中所说的破窗户问题。
(Page.141)要格外警惕从语意破坏封装性
对于语意上对于封装性的破坏,书中列出了五种情况,这里有不一一列出了。其实中心思想就是不要让类的内部实现影响外部调用,其中包括外部对类的调用顺序的限制。如果你发现封装的类,从外部调用的时候需要考虑内部实现,这时候其实就是破坏了类的封装性。
(Page.142)留意过于紧密的耦合关系
- 避免创建万能类(god class)
- 消除无关紧要的类
- 避免用动词命名的类
(Page.191)用错误处理代码来处理预期会发生的状况,用断言来处理绝不应该发生的状况
对于错误处理技术的使用,根据不同的使用场景给出了不同的建议。例如医疗设备控制软件,如果发生了医疗故障,为了保证患者的安全,应该立刻关闭程序等。防御式编程全部的重点在于防御那些未曾预料到的错误。
本章随后讨论了异常。对于异常的使用给出了几条建议。例如,
(Page.199)不能用异常来推卸责任
其实就是说,哪里出现了问题就在哪里解决,不要让规避问题的存在,或者说是遮掩问题。
不要在构造函数和析构函数中使用异常,除非你在同一地方把它们捕获
在构造函数中处理异常会让问题变的很麻烦,其中最重要的问题就是资源的泄漏问题。
书中提到了隔栏(barricade)这个词。并定义说“(Page.203)是一种容损策略(damage-cotainment strategy)”。我觉得目的就是为了把错误引起的损失控制在一定范围内。
书中其它部分谈到了几点关于产品发布和开发过程中,调试代码如何进行权衡。
第九章-伪代码编程过程
通过了解伪代码编程,然后再跳出整个软件开发的框框,我觉得写代码真成为了软件开发中一个不应该占用很多时间的一个环节。通过编写伪代码,可以把你的思路完全展现出来,作者的观点也很明确,当你什么时候发现伪代码已经无法更改的时候,再开始写真正的代码。
http://ohcoder.com/blog/2013/03/24/reading-code-complete/
http://ohcoder.com/blog/2013/04/07/reading-code-complete/
http://ohcoder.com/blog/2013/04/14/reading-code-complete/
(Page.240)隐式变量声明对于任何一种语言来说都是最具危险性的特性之一
作者给出的解决办法是:
- 关闭隐式声明
- 声明全部的变量
- 遵循某种命名规则
- 检查变量名
(Page.249)在循环开始之前再去初始化该循环里使用的变量,而不是在该循环所属的子程序的开始处初始化这些变量
对于这一条建议,作者的目的是能够在循环里正确的使用外部初始化的变量。比如你把一个变量的声明和使用这个变量的循环之间隔离的很远的话,当需要修改循环的时候比较不容易看到循环中使用的这个变量。
(Page.249)直到变量即将被使用时再为其赋值
这一条建议,我觉得很容易理解,主要就是为了让代码的初始化更醒目。
(Page.250)当对变量的作用域犹豫不决的时候,你应该倾向于选择该变量所能具有的最小作用域
这条建议,其实目的也是为了尽可能的降低变量的生存时间。
(Page.252)采用越晚的绑定时间会越有利
Page.261)一个好记的名字反映的通常都是问题,而不是解决方案。一个好名字通常表达的是“什么”(what),而不是“如何”(how)。
作者说,“(Page.261)一般而言,如果一个名字反映了计算的某些方面而不是问题本身,那么它反映的就是“how”而非“what”了。”
关于名字的长度,文中提到“(Page.262)当变量名的平均长度在10到16个字符的时候,调试程序所需花费的力气是最小的(1990)”。其实,我觉得,
Objective C
这门语言天生就挺符合这个要求的,不用你刻意去注意,哈哈。
对于变量中使用计算限定词,文中也给出了很好的建议。
(Page.263)如果你要用类似与Total、Sum、Average、Max、Min、Record、String、Pointer这样的限定词来修改某个名字,那么请记住把限定词加到限定词名字的最后。
- 避免使用“神秘数值(magic number)”
- 如果需要,可以使用硬编码的0和1
- 预防除零(devide-by-zero)错误
- 使类型转换变得明显
- 避免混合类型的比较
- 注意编译器的警告
本章后面的基本类型差不多都是围绕着上面这几点根据具体的类型特点展开来讲的。其中印象比较深刻的是关于浮点数的一条建议:
(Page.295)避免等量判断
很早之前掉进过这个坑。记得当时用了两个浮点数值进行等值判断,结果相等的概率非常的低,后来打印了一下数值,才发现掉到坑去里了,后来就长记性了,呵呵。
对于
C
语言的字符串的使用,有几个建议印象比较深刻。(Page.299)把C-Style字符串的长度声明为CONSTANT + 1
这一条建议可以避免循环数组时越界,当然这种写法在很多算法书中也很常见。
(Page.300)用null初始化字符串以避免无结束符的字符串
这条在平时初始化的时候也很受用。下面这条是关于分配内存并初始化的建议。
(Page.300)其次,在你动态分配字符串的时候,使用calloc()而不是malloc()来把它初始化为0。calloc()会负责分配内存,并把它初始化为0。malloc()只分配内存,并不执行初始化,…
对于定义枚举类型,文中给出了两条之前没有注意的点。
(Page.305)定义出枚举的第一项和最后一项,以便用于循环边界
这一点以前还真没注意过,以后要注意了。还有下一点,我觉得也是蛮有道理的,虽然表面上感觉有点小聪明的意思,呵呵。
(Page.305)把枚举类型的第一个元素留作非法值http://ohcoder.com/blog/2013/03/31/reading-code-complete/
(Page.348)设法组织代码,使依赖关系变得非常明显
我自己的理解是,在封装类的时候,从内部尽量使方法之间的依赖降到最低。在方法功能的实现上尽可能的做到功能单一,我觉得可以比较好的解决这个问题。应该尽可能的满足
KISS
原则吧。(Page.348)使子程序名能凸显依赖关系
这条原则的意思很明显,就是对所定义的方法要取个清晰的名字。
(Page.349)利用子程序参数明确显示依赖关系
这个原则也不用解释过多,书中提到了一种情况,就是把几个方法的参数定义成同一个类型,以此暗示这些方法在调用参数的时候需要留心它们之间的调用关系。我自己倒是觉得,或许还可以有一种情况,那就是规定调用的A方法能够产生调用B方法所需要的参数类型,这样A、B方法的调用也可以从外部有一个依赖关系。
(Page.350)用注释对不清晰的依赖关系进行说明
这种方法我认为属于下策,我觉得,任何通过注释进行说明的方式都是下策,我觉得好的代码应该尽量不依赖于注释,也许我的想法有些偏激,因为我觉得代码本身就是给人读的,如果通过代码仍让人琢磨不清,可能这段代码的设计偏离了
KISS
原则。当然,注释能够更好的辅助我们快速了解代码,我的意思并不是说写了注释的代码就是烂代码,不要走极端。(Page.350)用断言或者错误处理代码来检查依赖关系
对于顺序无关的语句应该如何书写。提到了两个大的要点,一个是(Page.351)使代码易于自上而下地阅读。另一个是(Page.352)把相关地语句组织在一起。这两个观点都很明显,为的就是使代码阅读起来更有条理。
第十五章——使用条件语句
- 首先写正常代码路径;再处理不常见情况。
- 确保对于等量的分支是正确的。请不要用“
>
”代替“>=
”或用“<
”代替“<=
”,这类似于在访问数组或者计算循环下标的时候犯下off-by-one(偏差一)错误。 - 把正常情况的处理放在
if
后面而不要放在else
后面 - 让
if
子句后面跟随一个有意义的语句 - 考虑
else
子句。如果你认为自己只需要一个简单的if
语句,那么请考虑你是否真的不需要一个if-then-else
语句。 - 测试
else
子句的正确性 - 检查
if
和else
子句是不是弄反了
- 按字母排序或按数字排序排列各种情况
- 把正常的情况放在前面
- 按执行频率排列
case
子句
上面提到的三种排列建议都比较好理解。接下来作者给出了几个使用
case
语句的小提示。(Page.361)简化每种情况对应的操作
其实,就是尽可能让每个
case
里的功能单一。(Page.361)不要为了使用case语句而刻意制造一个变量
这句话也很好理解,不要为了
case
而case
,凡事都有个度。(Page.363)把default子句只用于检查真正的默认情况
这句比较好理解,不要滥用
default
。(Page.372)如果你需要一个执行次数固定的循环,那么for循环就是一个很好的选择。
除此之外,书中对于何时用
while
循环代替for
循环也做了说明。
(Page.372)for循环的关键之处在于,你在循环头处把它写好后就可以忘掉它了,无需在循环的内部做任何事情去控制它。如果存在一个必须使执行从循环中跳出的条件,那么就应该改用while
循环。
(Page.374)在适当的情况下多使用for循环
Page.375)用“{”和“}”把循环中的语句括起来。任何时候都要在代码中使用括号。
这一习惯可以让别人更清晰的阅读代码。
(Page.375)避免空循环
我倒是没有这种习惯,一来觉得影响美观,二来会让代码看起来不是很清晰。
(Page.376)把循环内务操作要么放在循环的开始,要么放在循环的末尾。
这条建议目的觉得就是为了让书写代码更有规律可循,避免胡乱摆放代码。
(Page.376)一个循环只做一件事
这条建议很直观,遵守
KISS
原则。
对于循环的退出,书中也给出了一些建议。
(Page.377)不要为了终止循环而胡乱改动for循环的下标
这个错误我好像从来没干过。还有下一条建议从来没尝试过,从来没想过这么用,看了书中的例子才知道,原来还可以酱紫犯错误,也算是开眼了。
(Page.377)避免出现依赖于循环下标最终取值的代码
(Page.378)考虑使用安全计数器
(Page.383)把循环下标变量的作用域限制在本循环内
- 循环要尽可能的短,以便能够一目了然。如果你开始接受编写简单代码这一原则,那就很少会写出超过15或者20行的循环。
- 把嵌套限制在3层以内
- 把长循环的内容移到子程序里
- 要让长循环格外清晰
第十七章——不常见的控制结构
关于递归的使用,书中有一段话做了概要性的说明。
(Page.394)对于大多数问题,它所带来的解将会是及其复杂的——在那些情况下,使用简单的迭代通常会比较容易理解。因此要有选择地使用递归。
使用递归技巧方面,有几点还是需要注意的。
(Page.396)把递归限制在一个子程序内。循环递归(A调用B,B调用C,C调用A)非常危险,因为它很难检查。
(Page.397)留心栈空间
第十八章——表驱动法
“(Page.411)表驱动法是一种编程模式(scheme)——从表里面查找信息而不使用逻辑语句(if和case)。”。其实,看完整个章节,我的感受只有一条,就是用空间换时间,降低控制的复杂度。
关于从表里面查找信息的方法。书中描述了三种方法:
- 直接访问(Direct access)
- 索引访问(Indexed access)
- 阶梯访问(Stair-step access)
第十九章——一般控制问题
(Page.431)除了最简单的、要求语句按顺序执行的控制结构外,所有的控制结构都依赖于布尔表达式的求值(evaluation)。
随后,书中主要在讲使用布尔表达式时候的注意事项。这里就不多说了,都是一些常见的问题,点很多。但主导思想还是
KISS
原则。
接下来对复合语句或语句块做了简单的描述,并建议尽量用括号把条件表达清楚等等。
对于如何减少深层嵌套,书中做了技术总结。
- 重复判断一部分条件
- 转换成if-then-else
- 转换成else语句
- 把深层嵌套的代码提取诚单独的子程序
- 使用对象和多态派分(polymorphic dispatch)
- 用状态变量重写代码
- 用防卫子句来退出子程序,从而使代码的主要路径更为清晰
- 使用异常
- 完全重新设计深层嵌套的代码
软件同时拥有外在的和内在的质量特性。软件的外在特性包括(Page.463~Page.464),
- 正确性(Correctness)
- 可用性(Usability)
- 效率(Efficiency)
- 可靠性(Reliability)
- 完整性(Integrity)
- 适应性(Adaptability)
- 精确性(Accuracy)
- 健壮性(Robustness)
软件的内在特性包括(Page.464~Page.465)
- 可维护性(Maintainability)
- 灵活性(Flexibility)
- 可移植性(Portability)
- 可重用性(Reusability)
- 可读性(Readability)
- 可测试性(Testability)
- 可理解性(Understandability) 随后,书中说到在哪些方面可以改善软件质量(Page.466~Page.467)。
- 明确定义质量保证工作
- 测试策略
- 软件工程指南
- 非正式计数复查
- 正式计数复查
- 外部审查
软件质量目标
第二十一章——协同构建
- 用编码规范来支持结对编程
- 不要让结对编程编程旁观
- 不要强迫在简单的问题上使用结对编程
- 有规律地对结对人员和分配地工作任务进行轮换
- 鼓励双方跟上对方的步伐
- 确认两个人都能够看到显示器
- 不要强迫程序员与自己关系紧张的人组对
- 避免新手组合
- 指定一个组长
代码的
review
,然后大家坐在一起开会探讨。对于其中的角色,文中列出了五种(Page.486),- 主持人
- 作者
- 评论员(reviewer)
- 记录员
- 经理
主持人
其实就是管理详查这个事儿的,保证一定的进度。作者
就是码农本人。评论员
就是审查代码的人,记录员
就是记录问题的人。经理
就不用多说了,就是软件项目的负责人。文中有一条建议我觉得是保证顺利实施的关键一点,(Page.487)类似的,无论在任何情况下,详查的结果都不应当作为员工表现的评估标准,这种杀鸡取卵的行为不可取。在详查中被检验的代码仍处于开发阶段。对员工表现的评价应当基于最终产品,而不是尚未完成的工作。
最后在其他类型的协同开发实践中,作者提到
走查
。对于走查,引用书中的一段话描述,(Page.492)走查是一种很流行的复查方式,这个词的定义很随意,其流行在于某种程度上,人们把任何形式的复查都称为“走查”。- 单元测试(Unit testing)是将一个程序员或者一个开发团队所编写的,一个完整的类、子程序或者小程序,从完整的系统中隔离出来进行测试。
- 组件测试(Component testing)是将一个类、包、小程序或者其他程序元素,从一个更加完成的系统中隔离出来进行测试,这些被测试代码涉及到多个程序员或者多个团队。
- 集成测试(Integration testing)是对两个或更多的类、包、组件或者子系统进行的联合测试,这些组件由多个程序员或者开发团队所创建。这种测试通常在有了两个可以进行测试的类的时候就应该尽快开始,并且一直持续到整个系统开发完成。
- 回归测试(Regression testing)是指重复执行以前的测试用例,以便在原先通过了相同测试集合的软件中查找缺陷。
- 系统测试(System testing)是在最终的配置下(包括同其他软硬件系统的集成)运行整个软件。以便测试安全、性能、资源消耗、时序方面的问题,以及其他无法在低级集成上测试的问题。
关于测试的类型,通常分为两大类:黑盒测试(black-box testing)和白盒测试(white-box,或者玻璃盒glass-box)测试。
对于开发者测试来说,书中提到了几种推荐的方法(Page.503)。
- 对每一项相关的需求进行测试,以确保需求都已经被实现。
- 对每一个相关的设计关注点进行测试,以确保设计已经被实现。
- 用基础测试(basis testing)来扩充针对需求和设计的详细测试用例。
- 使用一个检查表,其中记录着你在本项目迄今为止所犯的,以及在过去的项目中所犯的错误类型。
文中说,(Page.503)首先写测试用例可以将从引入缺陷到发现并排除缺陷之间的时间缩减至最短。对于开发者测试,文中提到了一些局限性,这些局限性对于我来说,我觉得还是蛮准的(Page.504)。
- 开发者测试倾向于“干净测试”
- 开发者测试对于覆盖率有过于乐观的估计
- 开发者测试往往回忽略一些更复杂的测试覆盖率类型
在测试技巧这一节,文中提到几种测试类型。例如,
- (Page.505)结构化基础测试。
- (Page.509)数据流测试。
本章后面提到了几种测试支持工具。
- (Page.524)Diff工具
- (Page.524)测试数据生成器
- (Page.526)覆盖率监视器
- (Page.526)数据记录器/日志记录器
- (Page.526)符号调试器
- (Page.527)系统干扰器
- (Page.527)错误数据库
第二十三章——调试
在效率低下的调试方法中,作者列举了几种常见的调试方法,在调试之魔鬼指南类型中,作者列举了几种常见错误的调试方法(Page.539),
- 凭猜测找到缺陷
- 不要把时间浪费在理解问题上
- 用最唾手可得的方式修正错误
- 在动手之前先要理解问题
- 理解程序本身,而不仅仅是问题
- 验证对错误的分析
- 放松一下
- 保存初始的源代码
- 治本,而不是治标
- 修改代码时一定要有恰当的理由
- 一次只做一个改动
- 检查自己的改动
- 增加能暴露问题的单元测试
- 搜索类似的缺陷
- (Page.565)冗长的子程序,在面向对象的编程中,很少会需要用到长度超过一个屏幕的子程序。
- (Page.567)数据成员被设置为公用
- (Page.568)在子程序调用前使用了设置代码(setup code),或在调用后使用了收尾代码(takedown code)
- (Page.569)程序中的一些代码似乎是在将来的某个时候才会用到的
- 保存初始代码
- 重构的步伐请小些
- 同一时间只做一项重构
- 把要做的事情一条条列出来
- 设置一个停车场
- 多使用检查点
- 利用编译器警告信息
- 重新测试
- 增加测试用例
- 检查对代码的修改
- 根据重构风险级别来调整重构方法
第二十五章——代码调整策略
(Page.592)Pareto法则也就是众所周知的80/20法则。它讲述的是你可以用20%的努力取得80%的成绩。这一法则适用于程序设计之外的众多领域,它对程序优化也绝对有效。
- 在高级语言中,减少代码的行数就可以提升所生成的机器代码的运行速度,或是减少其资源占用——错误!
- 特定运算可能比其他的快,代码规模也较小——错误!
- 应当随时随地进行优化——错误!
- 程序的运行速度同其正确性同等重要——错误!
(Page.596)程序员应当使用高质量的设计,把程序编写正确。使之模块化并易于修改,将让后期的维护工作变得很容易。
关于这句话的理解,我觉得对于软件的优化来说,设计的重要性要大于局部的代码改动。
(Page.604)我得到了一个教训,如果没有测量性能变化,那么你想当然的优化结果不过是代码变得更为晦涩难懂了。
第二十六章——代码调整技术
本章主要从代码上对优化做了讲解。首先对于逻辑判断语句提出了一些建议。
- (Page.610)在知道答案后停止判断。例如,在
for
循环中一旦得到结果就跳出循环,减少循环次数。 - (Page.612)按照出现频率来调整判断顺序。例如,在
if-else
语句和switch-case
语句中,把预计频繁出现的判断尽量排到前面。
对于循环,也给出了一些建议。
- (Page.616)将判断外提。如果在循环运行时某个判断结果不会改变,你就可以把这个判断提到循环的外面,从而避免在循环中进行判断。
- (Page.617)合并。合并(jamming),或融合(fusion),就是把两个对相同一组元素进行操作的循环合并在一起。此举所带来的好处就是把两个循环的总开销减少至单个循环的开销。
- (Page.620)尽可能的减少在循环内部做的工作。
- (Page.621) 哨兵值。当循环的判断条件是一个符合判断的时候,你可以通过简化判断来节省代码运行时间。如果该循环是一个查找循环,简化方法之一就是使用一个哨兵值(sentinel value),你可以把它放到循环范围的末尾,从而保证循环一定能够中止。
- (Page.623)把最忙的循环放到最内层。
- (Page.623)削减强度。削减强度意味着用多次轻量级运算(例如加法)来代替一次代价高昂的运算(例如乘法)。
在数据使用上,同样给出了一些优化的建议。
- (Page.625)使用整型数而不是浮点数。整型数的加法和乘法要比浮点数的相应运算快很多。
- (Page.625)数组维度尽可能少。
- (Page.626)尽可能减少数组引用。
- (Page.627)使用辅助索引的意思就是天假相关数据,使得对某种数据类型的访问更为高效。书中给出了两种索引方式,一种是字符串长度索引。另一种是独立的平行的索引结构。
- (Page.628)使用缓存机制。缓存机制就是把某些值存起来,使得最常用的值会比不太常用的值更容易被获取。
第二十七章——程序规模对构建的影响
本章开始描述了交流与规模的关系,提出(Page.650)…二者的关系并不是加性的而是乘性的。即交流路径的条数大致正比于人数的平方。
根据项目规模的不同,会对错误产生不同的影响。(Page.651)项目的规模既会影响错误的数量,也会影响错误的类型。坦白说,这句话中所说的项目规模会对错误数量产生影响很自然可以想到,但是说也会影响错误的类型,我之前是没有想到的。
书中谈到项目规模对生产率影响一节时,引用到这样一句话,我觉得对于移动互联网的开发也是蛮适合的。这句话是这样说的(Page.653)…,完成项目的小型团队的生产率摇臂大型团队高出39%,那么这些团队各有多少人呢?答案是,小项目两个人,大项目三个人(1984)。之前看《打造Facebook》那本书的时候,作者提到在Facebook内部开发一个新项目,一般人数也很少。例如他自己主导开发的那个项目,一开始也只有三个人。
对于项目规模对开发活动的影响一节中,文中指出(Page.655)规模相近的项目会执行相似的活动,但是随着项目规模不同,其所需要进行的活动的种类也会有明显的差异。最后谈论了一下不同级别的规模与方法论之间的关系。其实,本章讲的都比较概括,不过中心思想很明确,主要就是描述了因为程序规模的不同对于构建软件不同方面的影响。
第二十八章——管理构建
在编码方面,提出了一些建议,其中对于定制标准方面,书中给出了一条建议,我觉得可以直接用于实践,
(Page.662)如果项目中有人要制定标准,那么应该由一位受人尊敬的架构师来做,而不应该由管理者来做。在软件项目中,“专家层”起的作用至少与“管理层”相同。
其实,像这种直接面向技术层面的问题,管理者或许没有资深的技术人员更专业。对于良好的编码实践要比呆板的标准更容易实行。书中给出了几条建议(Page.662~Page.664)。
- 给项目的每一部分分派两个人
- 逐行复查代码
- 要求代码签名
- 安排一些好的代码示例供人参考
- 强调代码是公有财产
- 奖励好代码
- 一份简单的标准
上面的建议的好处也比较明显,就不展开说了。
接下来讲到配置管理,什么是配置管理?
(Page.664)配置管理是“系统化地定义项目工件(project arifacts)和处理变化,以使项目一直保持其完整性”的实践活动。它的另一种说法是“变更控制”。其中的技术包括评估所提交的更改、追踪更改、保留系统在不同时间点的各历史版本。
对于构建过程中需求的变更和设计的变更,书中给出了一种方法是,
(Page.666)…,记下所有的想法和建议,不管它实现起来有多容易。把它记录下来,直到你有时间去处理它们。到那时,把它当做整体(group)来看待,从中选中最有益的一些变更来加以实施。
即便如此,对于软件的变更也会是一大难题。估计很多朋友都在网上看到过一些有趣的漫画,内容大概是项目经理让程序员不停的改动需求,直到程序员崩溃…。书中对此现象的评价说(Page.667)缺乏规范的变更控制是当今软件业面临的主要管理难题之一。看来,变更是一个世界性的难题,呵呵。
对于软件代码变更的管理,书中提到了版本管理软件的使用。版本管理软件不但可以有效的管理代码变更,还可以对代码起到备份的作用,不会轻易丢失代码。
对于评估构建的进度方面,书中提到了一些评估项目的方法(Page.671~Page.672)。
- 建立目标
- 为评估留出时间,并且做出计划
- 清楚地说明软件需求
- 在底层细节层面进行评估
- 使用若干不同的评估方法,并且比较其结果
- 定期做重新评估
对于项目进度影响最大的原因是(Page.674)…所开发的程序的规模。这一点很容易理解,呵呵。
在度量这一节中,文中提出了对项目进行度量的两个根本原因。
- (Page.677)任何一种项目特征(attribute)都是可以用某种方法来度量的,而且总会比根本不度量好得多。
- (Page.678)反对度量就是认为最好不要去了解项目中到底在发生什么。