正确性
正确性是指软件按照需求正确执行任务的能力。这里“正确性”的语义涵盖了“精确性”。正确性无疑是第一重要的软件质量属性。如果软件运行不正确,将会给用户造成不便甚至损失。技术评审和测试的第一关都是检查工作成果的正确性。
正确性说起来容易做起来难。因为从“需求开发”到“系统设计”再到“实现”,任何一个环节出现差错都会降低正确性。机器不会主动欺骗人,软件运行出错通常都是人造成的,所以不要找借口埋怨机器有毛病。开发任何软件,开发者都要为“正确”两字竭尽全力。
健壮性
健壮性是指在异常情况下,软件能够正常运行的能力。正确性与健壮性的区别是:前者描述软件在需求范围之内的行为,而后者描述软件在需求范围之外的行为。可是正常情况与异常情况并不容易区分,开发者往往要么没想到异常情况,要么把异常情况错当成正常情况而不做处理,结果降低了健壮性。用户才不管正确性与健壮性的区别,反正软件出了差错都是开发方的错。所以提高软件的健壮性也是开发者的义务。
健壮性有两层含义:一是容错能力,二是恢复能力。
容错是指发生异常情况时系统不出错误的能力,对于应用于航空航天、武器、金融等领域的这类高风险系统,容错设计非常重要。
容错是非常健壮的意思,比如UNIX的容错能力很强,很难使系统出问题。而恢复则是指软件发生错误后(不论死活)重新运行时,能否恢复到没有发生错误前的状态的能力。
从语义上理解,恢复不及容错那么健壮。
例如,某人挨了坏蛋一顿拳脚,特别健壮的人一点事都没有,表示有容错能力;比较健壮的人,虽然被打倒在地,过了一会还能爬起来,除了皮肉之痛外倒也不用去医院,表示恢复能力比较强;而虚弱的人可能短期恢复不过来,得在病床上躺很久。
恢复能力是很有价值的。Microsoft公司早期的窗口系统,如Windows 3.x和Windows 9x,动不动就死机,其容错性的确比较差。但它们的恢复能力还不错,机器重新启动后一般都能正常运行,看在这个份上,人们也愿意将就着用。
可靠性
可靠性不同于正确性和健壮性,软件可靠性问题通常是由于设计中没有料到的异常和测试中没有暴露的代码缺陷引起的。可靠性是一个与时间相关的属性,指的是在一定环境下,在一定的时间段内,程序不出现故障的概率,因此是一个统计量,通常用
平均无故障时间(MTTF, mean-time to fault)来衡量。
可靠性本来是硬件领域的术语。比如某个电子设备在刚开始工作时挺好的,但由于器件在工作中其物理性质会发生变化(如发热、老化等),慢慢地系统的功能或性能就会失常。所以一个从设计到生产完全正确的硬件系统,在工作中未必就是可靠的。人们有时把可靠性叫做稳定性。
软件在运行时不会发生物理性质的变化,人们常认为如果软件的某个功能是正确的,那么它一辈子都是正确的。可是我们无法对软件进行彻底的测试,无法根除软件中潜在的错误。平时软件运行得好好的,说不准哪一天就不正常了,如有千年等一回的“千年虫”问题、司空见惯的“内存泄露”问题、“误差累积”问题,等等。因此把可靠性引入软件领域是很有意义的。
软件可靠性分析通常采用统计方法,遗憾的是目前可供第一线开发人员使用的成果很少见,大多数文章限于理论研究。我曾买了一本关于软件可靠性的著作,此书充满了数学公式,实在难以看懂,更不知道怎样应用。请宽恕我的愚昧,我把此“天书”给“供养”起来,没敢用笔画一处记号。
口语中的可靠性含义宽泛,几乎囊括了正确性、健壮性。只要人们发现系统有毛病,便归结为可靠性差。从专业角度讲,这种说法是不对的,可是我们并不能要求所有的人都准确地把握质量属性的含义。
有必要搞清楚“故障”和“错误”这两个容易混淆的概念。
在《现代英汉词典》里,“故障(Fault
)”一词的定义是:
使设备、部件或元件不能按所要求的方式运行的一种意外情况,可能是物理的也可能是逻辑的。
那些潜伏在代码中的错误往往是不明显的,之所以在测试的时候没有暴露,是因为测试时的环境和条件不足以使之暴露,更何况我们无法对代码进行最彻底的测试。由此可见,故障是在经过日积月累,满足了一定的条件之后才出现的。例如,“千年虫”问题,“内存泄漏(吃内存)”导致内存耗尽,“误差累积”导致计算错误进而导致连锁反应,“性能开销累积”导致性能显著下降,等等。因此,故障通常都是不可预料的、灾难性的。
“错误”的含义要广泛得多,例如,语法错误、语义错误、文件打开失败、动态存储分配失败等。一般说来,程序错误是可以预料的,因此可以预设错误处理程序,运行时这些错误一旦发生,就可以调用错误处理程序把它干掉,程序还可以继续运行。因此,错误的结果一般来说不是灾难性的。
性能
性能通常是指软件的“时间—空间”效率,而不仅是指软件的运行速度。人们总希望软件的运行速度快些,并且占用资源少些。旧社会地主就是这么对待长工的:干活要快点,吃得要少点。
程序员可以通过优化数据结构、算法和代码来提高软件的性能。算法复杂度分析是很好的方法,可以达到“未卜先知”的功效。
性能优化的目标是“既要马儿跑得快,又要马儿吃得少”,关键任务是找出限制性能的“瓶颈”,不要在无关痛痒的地方瞎忙活。例如,在大学里当教师,光靠卖力气地讲课或者埋头做实验,职称是升不快的。有些人找到了突破口,一年之内“造”几十篇文章,争取破格升副教授、教授。在学术上走捷径,这类“学者”的质量真让人担忧。
性能优化就好像从海绵里挤水一样,你不挤,水就不出来,你越挤海绵越干。有些程序员认为现在的计算机不仅速度越来越快,而且内存越来越大,因此软件性能优化的必要性下降了。这种看法是不对的,殊不知随着机器的升级,软件系统也越来越庞大和复杂了,性能优化仍然大有必要。最具有代表性的是三维游戏软件,如《Delta Force》、《古墓丽影》、《反恐精英》等,如果不对软件(关键是游戏引擎)做精益求精的优化,要想在一台普通的PC上顺畅地玩游戏是不太可能的。
易用性
易用性是指用户使用软件的容易程度。现代人的生活节奏快,干什么事都可能想图个方便,所以把易用性作为重要的质量属性无可非议。
导致软件易用性差的根本原因是开发人员犯了“错位”的毛病:他以为只要自己用起来方便,用户也一定会满意。俗话说“王婆卖瓜,自卖自夸”。当开发人员向用户展示软件时,常会得意地讲:“这个软件非常好用,我操作给你看,……是很好用吧!”
软件的易用性要让用户来评价。如果用户觉得软件很难用,开发人员不要有逆反心理:哪里找来的笨蛋!
其实不是用户笨,是自己开发的软件太笨了。当用户真的感到软件很好用时,一股温暖的感觉就会油然而生,于是就会用“界面友好”、“方便易用”等词来夸奖软件的易用性。
清晰
清晰意味着工作成果易读、易理解,这个质量属性表达了人们的一种质朴的愿望:让我花钱买它或者用它,总得让我看明白它是什么东西。我小时候的一个伙伴在读中学时就因搞不明白电荷为什么还要分“正”和“负”,觉得很烦恼,便早早地辍学当了工人。
开发人员只有在自己思路清晰的时候才可能写出让别人易读、易理解的程序和文档。可理解的东西通常是简洁的。一个原始问题可能很复杂,但高水平的人就能够把软件系统设计得很简洁。如果软件系统臃肿不堪,它迟早会出问题。所以简洁是人们对工作“精益求精”的结果,而不是潦草应付的结果。
在生活中,与简洁对立的是“啰唆”。废话大师有句名言:“如果我令你过于轻松地明白了,那你一定是误解了我的意思。”中国小说中最“婆婆妈妈”的男人是唐僧。有一项民意调查:如果世上只有唐僧、孙悟空、猪八戒和沙僧这四类男人,你要嫁给哪一类?请列出优先级。调查结果表明,现代女性毫不例外地把唐僧摆在最后。
很多人在读研究生时有一种奇怪的体会:如果把文章写得很简洁,让人很容易理解,投稿往往中不了,只有加上一些玄乎的东西,把本来简单的东西弄成复杂的,才会增加投稿的命中率。虽然靠这种做法可能有效,可千万不要把此“经验”应用到产品的开发中!
安全性
这里的安全性是指信息安全,英文是Security而不是Safety。安全性是指防止系统被非法入侵的能力,既属于技术问题又属于管理问题。信息安全是一门比较深奥的学问,其发展是建立在正义与邪恶的斗争之上的。这世界似乎不存在绝对安全的系统,连美国军方的系统都频频遭黑客入侵。如今全球黑客泛滥,真是“道高一尺,魔高一丈”啊!
对于大多数软件产品而言,杜绝非法入侵既不可能也没有必要。因为开发商和客户愿意为提高安全性而投入的资金是有限的,他们要考虑值不值得。究竟什么样的安全性是令人满意的呢?
一般地,如果黑客为非法入侵花费的代价(考虑时间、费用、风险等多种因素)高于得到的好处,那么这样的系统就可以认为是安全的。
可扩展性
可扩展性反映了软件适应“变化”的能力。在软件开发过程中,“变化”是司空见惯的事情,如需求、设计的变化,算法的改进、程序的变化等。
由于软件是“软”的,是否它天生就容易修改以适应“变化”?
关键要看软件的规模和复杂性。
如果软件规模很小,问题很简单,那么修改起来的确比较容易,这时就无所谓“可扩展性”了。要是软件的代码只有100行,那么“软件工程”也就用不着了。
如果软件规模很大,问题很复杂,倘若软件的可扩展性不好,那么该软件就像用卡片造成的房子,抽出或者塞进去一张卡片都有可能使房子倒塌。可扩展性是系统设计阶段重点考虑的质量属性。
兼容性
兼容性是指两个或两个以上的软件相互交换信息的能力。由于软件不是在“真空”里应用的,它需要具备与其他软件交互的能力。例如,两个字处理软件的文件格式兼容,那么它们都可以操作对方的文件,这种能力对用户很有好处。国内金山公司开发的字处理软件WPS就可以操作Word文件。
兼容性的商业规则是:弱者设法与强者兼容,否则无容身之地;强者应当避免被兼容,否则市场将被瓜分。如果你经常看香港拍的“黑帮”影片,你就很容易明白这个道理。所以WPS一定要与Word兼容,否则活不下去。但是Word绝对不会与WPS兼容,除非WPS在中国称老大。
可移植性
软件的可移植性指的是软件不经修改或稍加修改就可以运行于不同软硬件环境(CPU、OS和编译器)的能力,主要体现为代码的可移植性。编程语言越低级,用它编写的程序越难移植,反之则越容易。这是因为,不同的硬件体系结构(如Intel CPU和SPARC CPU)使用不同的指令集和字长,而OS和编译器可以屏蔽这种差异,所以高级语言的可移植性更好。
C++/C是一种中级语言,因为它具有灵活的“位操作”能力(因此具有硬件操作能力),而且可以直接嵌入汇编代码。但是C++/C并不依赖于特定的硬件,因此比汇编语言可移植性好。
Java是一种高级语言,Java程序号称“一次编译,到处运行”,具有100%的可移植性。为了提高Java程序的性能,最新的Java标准允许人们使用一些与平台相关的优化技术,这样优化后的Java程序虽然不能“一次编译,到处运行”,仍然能够 “一次编程,到处编译”。
一般地,软件设计时应该将“设备相关程序”与“设备无关程序”分开,将“功能模块”与“用户界面”分开,这样可以提高可移植性。
本文节选自《高质量程序设计指南:C++/C语言》
电子工业出版社出版