毕业前准备校招面试的时候,和同学一起刷《高质量C++/C编程指南》,以为作者林锐是个严肃的学者(老学究)。《软件工程思想》彻底颠覆了我对作者的认知。作者不仅技术文章写的好,文采也很出众,思维、行为都很活跃,很多思想在二十年后的今天仍然适用。
下面是《软件工程思想》的精华摘录,原汁原味的鸡汤,闲暇时不妨品下,可能会有些许收获。如果想拜读原文,可以从这里下载:软件工程思想。
一、软件工程的基本观念
- 对开发人员而言,如果非得在质量与生产率之间分个主次不可,那么应该是质量第一,生产率第二。这是因为:
- 质量直接体现在软件的每段程序中,高质量自然是开发人员的技术追求,也是职业道德的要求。
- 高质量对所有的用户都有价值,而高生产率只对开发方有意义。
- 如果一开始就追求高生产率,容易使人急功近利,留下隐患。宁可进度慢些,也要保证每个环节的质量,以图长远利益。
-
软件开发中的三种基本策略:“复用”、“分而治之”、“优化——折衷”。
-
为了使整个组织具有最好的战斗力,我们要重用几个人,照顾一些人,在万不得已的情况下委屈一批人。
- 慎用技巧:一个局部的优点对整个系统而言是微不足道的,而一个错误则可能是致命的。
二、程序员与程序经理
-
微软公司在选择经理人员时,总是把他们的技术知识和运用技术去赚钱的能力放在首位。
-
只有两个人的办公室也要设正主任和副主任。如果碰巧正主任姓傅,副主任姓郑,还会斗个没完没了。
-
公平: 如果程序经理发现有两个程序员趴在机器旁睡觉,不能只对其中一个大声吼叫:“你一编程就想睡觉,看看人家,在睡觉时都想着编程。”
-
我搞了八年的软件开发,没做出象样的软件来。倒是有同行意外发现我的文笔不错,是当作家的料。我发现自己在不该开花的地方结了一颗瘦涩的果子。
-
工作在第一线的程序员与程序经理应该意识到:好兵好将都不是天生的,是后天练出来的;既要学会冷静地分析问题,又要充满激情地去工作。
-
技术级别与管理级别的映射:
-
“迷信”是傻子碰到骗子的结果。傻是内因,被骗是外因。傻子碰到好人未必能做出好事,傻子碰到另一个骗子就会做出另一件傻事。
三、项目计划与质量管理
-
软件的项目计划重在“准确”而非“快速”。 做项目计划,如同给一个待出生的婴儿写传记那样困难。如果允许项目结束后再写计划,那就轻松多了,并且可以100% 地准确。 只有“知已知彼”才能做出合理的项目计划。这里“知彼”是指要了解项目的规模、难度与时间限制。“知已”是指要了解有多少可用资源,如可调用的程序员有几个?他们的水平如何?软硬件设施如何?
-
软件的高质量并不是“管理”出来的,实质上是设计出来的,质量的管理只是一种预防和认证的手段而已。
-
很多人认为戒烟很困难,但马克·吐温曾说:“戒烟很容易,我一年就戒几十次。”
-
“零缺陷”质量管理至少有两个核心内容:一是高目标,二是可执行的规范。做一个项目通常需要多个人的协作。假设项目的总质量(最高为1)是十个开发人员的工作质量之积。如果每个人的质量目标是0.95,那么十个人的累积质量不会超过0.19。如果每个人的质量目标是0.9分,那么十个人的累积质量不会超过0.03。只有每个人都做到1,项目总质量才会是1。如果没有高目标,人的堕落就很快。如果没有“零缺陷”的质量目标,也许缺陷就会成堆。
-
“运行正确”的程序不见得就是高质量的程序。这个程序也许运行速度很低并且浪费内存;也许代码写得一塌糊涂,除了开发者本人谁也看不懂也不会使用。正确性只是反映软件质量的一个因素而已。
-
优化的关键工作是找出限制性能与效率的“瓶颈”,不要在无关痛痒的地方瞎忙乎。
-
软件的高质量主要是设计出来的,不是“管”出来的,更不能依赖质量检查。为此程序员要充分了解软件的质量因素,只有提高设计水平,才能开发出高质量的软件。
四、可行性分析与需求分析
-
可行性分析的四大要素:经济、技术、社会环境和人。
-
最好的分工是让“人物”当领导,“人才”做第一线的开发人员,“人手”做行政人员,“人渣”负责行贿。
-
最好为每个需求注释“为什么”,这样可让程序员了解需求的本质,以便选用最合适的技术来实现此需求。
五、系统设计
-
人体中最糟糕的模块设计之一是嘴巴,嘴巴将最有价值但毫无相干的几种功能如吃饭、说话、亲吻混为一体,使之无法并行处理,真乃人类之不幸。
-
模块设计优劣的三个特征因素:“信息隐藏”、“内聚与耦合”和“封闭——开放性”。
-
内聚和耦合是密切相关的,与其它模块存在强耦合的模块通常意味着弱内聚,而强内聚的模块通常意味着与其它模块之间存在弱耦合。
六、C++面向对象程序设计
-
使用继承的场景: 若在逻辑上B是A的“一种”(a kind of ),则允许B继承A的功能。如男人(Man)是人(Human)的一种,男孩(Boy)是男人的一种。那么类Man可以从类Human派生,类Boy可以从类Man派生。
-
使用组合的场景: 若在逻辑上A是B的“一部分”(a part of),则不允许B继承A的功能,而是要用A和其它东西组合出B。例如眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head应该由类Eye、Nose、Mouth、Ear组合而成,不是派生而成。
-
派生类的功能可以被基类指针引用,这叫向后兼容,可以提高程序的可扩充性和可维护性。以前写的程序可以被将来写的程序调用不足为奇,但是将来写的程序可以被以前写的程序调用那可了不起。
-
没有人强迫你采用何种命名法,但有一点应该做到:自己的程序命名必须一致。
-
不要编写集多种功能于一身的函数,在函数的返回值中,不要将正常值和错误标志混在一起。
七、测试与改错
-
医生可以把他的错误埋葬在地下了事,但程序员不能。我们必须要学会测试与改错,并且把测试与改错工作做好。
-
测试的目的是为了发现尽可能多的缺陷。 如果说测试的目的是为了说明程序中没有缺陷,那么测试人员就会向这个目标靠拢,因而下意识地选用一些不易暴露错误的测试示例。这样的测试是虚假的。测试并不仅是个技术问题,更是个职业道德问题。
-
测试只能证明缺陷存在,而不能证明缺陷不存在。 “彻底地测试”只是一种理想。在实践中,测试要考虑时间、费用等限制,不允许无休止地测试。
-
测试有助于提高软件的质量,但是提高软件的质量不能依赖于测试。测试与质量的关系很象在考试中“检查”与“成绩”的关系。学习好的学生,在考试时通过认真检查能减少因疏忽而造成的答题错误,从而“提高”了考试成绩(取得他本来就该得的好成绩)。而学习差的学生,他原本就不会做题目,无论检查多么细心,也不能提高成绩。所以说,软件的高质量是设计出来的,而不是靠测试修补出来的。
-
开发人员应该执行“白盒”测试,即测试源程序的逻辑结构以及实现细节(“白盒”是指看得见程序的内部结构)。而独立测试小组应该执行“黑盒”测试,即按照规格说明来测试程序是否符合要求(“黑盒”是指看不见程序的内部结构)。比如在测试一个模块时,“白盒”测试方法要对模块的所有代码进行单步跟踪测试。而“黑盒”测试方法只需测试模块的接口是否符合要求,它关心程序的外部表现而不是内部的实现细节。
-
测试不能依赖于开发人员或者测试小组中的任意一方,必须是双方共同参与。
-
易用性测试:只有30%的用户会查阅用户手册。一般认为,如果用户不翻阅手册就能使用软件,那么表明这个软件具有较好的易用性。
-
文档测试:文档中很多内容对开发者可能是“显然”的,但对用户而言不见得都是“显然”的。
-
一个错误自身也许很微小,但是程序存在错误这件事很严重。能否做好测试与改错工作,思想认识和办事态度是最关键的。 程序员应该把测试当成份内之事,不要依赖于外界的“黑盒测试”。
八、维护与再生工程
- 一些学者将软件维护划分为主要的三类:
- 纠错性维护(Corrective maintenance),即解决现有软件中的缺陷。
- 适应性维护(Adaptive maintenance),比如新的硬件设备、操作系统引入而带来的维护工作。
- 完善性维护(Perfective maintenance),新需求引入。
- 软件维护是既破财又费神的工作。看得见的代价是那些为了维护而投入的人力与财力。而看不见的维护代价则更加高昂,我们称之为“机会成本”,即为了得到某种东西所必须放弃的东西。把很多程序员和其它资源用于维护工作,必然会耽误新产品的开发甚至会丧失机遇,这种代价是无法估量的。