软件质量保证与测试第8章 高质量编程_第1页
软件质量保证与测试第8章 高质量编程_第2页
软件质量保证与测试第8章 高质量编程_第3页
软件质量保证与测试第8章 高质量编程_第4页
软件质量保证与测试第8章 高质量编程_第5页
已阅读5页,还剩44页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第8章 高质量编程1.内容提要8.1 代码风格 8.1.1 程序的书写格式 8.1.2 Windows程序命名规则8.1.3 共性规则8.1.4 表达式和基本语句 8.2 函数设计规则 8.2.1 函数外部特性的注释规则 8.2.2 参数规则 8.2.3 返回值的规则 8.2.4 函数内部的实现规则 8.3 提高程序质量的技术 8.3.1 内存管理规则 8.3.2 面向对象的设计规则 8.4 代码审查 8.4.1 代码审查的主要工作 8.4.2 代码审查的流程 8.4.3 Java代码审查的常见错误 8.5 小结 2.8.1 代码风格统一编程风格的意义很大,是一个优秀而且职业化的开发团队所必需的素质。增加开发过程代码的强壮性、可读性、易维护性。减少有经验和无经验开发人员编程所需的脑力工作,为软件的良好维护性打下好的基础。通过人为以及自动的方式对最终软件应用质量标准,使新的开发人员快速适应项目氛围。支持项目资源的复用:允许开发人员从一个项目区域移动到另一个,而不需要重新适应新的子项目团队的氛围。 程序的书写格式版本的声明格式////Copyright@2011,北京侏罗纪公司XX部//Allrightsreserved.////文件名称:filename.h//文件标识:见配置管理计划书//摘要:简要描述本文件的内容///当前版本:2.1//作者:输入作者名字//完成日期:2011年3月20日////取代版本:2.0//原作者:输入原作者名字//完成日期:2011年2月10日//4.头文件的书写格式头文件必须包含下列内容:头文件开头处的版权和版本声明。预处理块。函数和类结构声明等。//版权和版本声明见示例1-1。#ifndefGRAPHICS_H//防止graphics.h被重复引用#defineGRAPHICS_H#include<math.h>//引用标准库的头文件 …#include“myheader.h”//引用非标准库的头文件 …voidFunction1(…);//全局函数声明 …classBox //类结构声明{ …};#endif5.定义文件的书写格式定义文件的书写格式:必须包含三部分内容:定义文件开头处的版权和版本声明;对一些头文件的引用;程序的实现体(包括数据和代码)。//版权和版本声明见示例1-1,此处省略。#include“graphics.h” //引用头文件 …//全局函数的实现体voidFunction1(…){ …}//类成员函数的实现体voidDraw(…){ …}6.空行的使用//空行//空行voidFunction1(…){ …}//空行//空行voidFunction2(…){ …}//空行//空行voidFunction3(…){ …}//空行While(condition){ statement1; //空行 if(condition) { statement2; } else { statement3; }//空行 statement4;}7.

VoidFunc1(intx,inty,intz);//良好的风格voidFunc1(intx,inty,intz);//不良的风格if(year>=2000)//良好的风格if(year>=2000)//不良的风格if((a>=b)&&(c<=d))//良好的风格if(a>=b&&c<=d)//不良的风格for(i=0;i<10;i++)//良好的风格for(i=0;i<10;i++)//不良的风格for(I=0;I<10;i++)//过多的空格x=a<b?a:b;//良好的风格x=a<b?a:b;//不好的风格int*x=&y;//良好的风格int*x=&y;//不良的风格array[5]=0;//不要写成array[5]=0;a.Function();//不要写成a.Function();b->Function();//不要写成b->Function();代码行内的空格 Windows程序命名规则匈牙利命名法是一种编程时的命名规范。基本原则是:变量名=属性+类型+对象描述,其中每一对象的名称都要求有明确含义,可以取对象名字全称或名字的一部分。命名要基于容易记忆容易理解的原则。保证名字的连贯性是非常重要的。举例来说,表单的名称为form,那么在匈牙利命名法中可以简写为frm,则当表单变量名称为Switchboard时,变量全称应该为frmSwitchboard。这样可以很容易从变量名看出Switchboard是一个表单,同样,如果此变量类型为标签,那么就应命名成lblSwitchboard。可以看出,匈牙利命名法非常便于记忆,而且使变量名非常清晰易懂,这样,增强了代码的可读性,方便各程序员之间相互交流代码。据说这种命名法是一位叫CharlesSimonyi的匈牙利程序员发明的,后来他在微软呆了几年,于是这种命名法就通过微软的各种产品和文档资料向世界传播开了。现在,大部分程序员不管自己使用什么软件进行开发,或多或少都使用了这种命名法。9.常用的数据类型前缀前缀类型例子bBOOLbIsParentby,byteBYTEbyFlag,byteFlagchcharchTextfn函数变量fnCallbackhHANDLE(句柄)hWndiintiValuenintnValueuunsignedintuFlagdwDWORDdwDatap指针pBuffersz,str字符串szBufferlpstr,lpszLPSTRlpstrMessagewWORDwDatax,y坐标xPos,yPosm_类成员变量m_bFlag,m_nValg_全局变量g_bFlag,g_nMsg10.常用的控件名前缀前缀控件类型frm,wnd窗口cmd,btn按钮cmb,combo下拉式列表框txt文本输入框lbl标签grdGrid,网格scr滚动条lst列表框frame框架 共性规则[提示1]较短的单词可通过去掉“元音”形成缩写;较长的单词可取单词的头几个字母形成缩写;一些单词有大家公认的缩写。示例:如下单词的缩写能够被大家基本认可。temp可缩写为tmp;flag可缩写为flg;statistic可缩写为stat;increment可缩写为inc;message可缩写为msg;[提示2]应该在源文件的开始之处,对文件中所使用的缩写或约定,特别是特殊的缩写,进行必要的注释说明。标识符最好采用英文单词或其组合,便于记忆和阅读,可望文知意,不必进行“解码”。不能使用汉语拼音来命名。程序中的英文单词一般不会太复杂,用词应当准确。例如不要把CurrentValue写成NowValue。 表达式和基本语句1.表达式与复合表达式[示例]如下表达式,考虑不周就可能出问题,也较难理解。

*stat_poi+++=1; *++stat_poi+=1;应分别改为如下。 *stat_poi+=1;

stat_poi++;//此二语句功能相当于“*stat_poi+++=1;”和

++stat_poi;*stat_poi+=1;//此二语句功能相当于“*++stat_poi+=1;”13.if语句假设布尔变量名字为flag,它与零值比较的标准if语句如下:if(flag) //表示flag为真if(!flag) //表示flag为假其它的用法都属于不良风格,例如:if(flag==TRUE) if(flag==1) if(flag==FALSE)if(flag==0) //我觉得应该采用if(flag==TRUE)来表示,赋值用flag=TRUE;//因为不同操作系统的TRUE和FALSE不一样,如WINDOWS里TRUE是1而有些系统//TRUE是0cyj应当将整型变量用“==”或“!=”直接与0比较。假设整型变量的名字为value,它与零值比较的标准if语句如下:if(value==0)if(value!=0)不可模仿布尔变量的风格而写成if(value) //会让人误解value是布尔变量if(!value)不可将浮点变量用“==”或“!=”与任何数字比较。14.循环语句的效率For(row=0;row<100;row++){for(col=0;col<5;col++){sum=sum+a[row][col];}}for(col=0;col<5;col++){for(row=0;row<100;row++){sum=sum+a[row][col];}}15.建议【建议】如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。例如,下面代码:for(I=0;i<N;I++){if(condition)DoSomething();ElseDoOtherthing();}if(condition){for(i=0;i<N;i++)DoSomething();}else{for(i=0;i<N;i++)DoOtherthing();}16.建议【建议】for语句的循环控制变量的取值采用“半开半闭区间”写法。例如for(x=0;x<N;x++){ …}for(x=0;x<=N-1;x++){ …}17.C++类中的常量不能在类声明中初始化const数据成员。以下用法是错误的,因为类的对象未被创建时,编译器不知道SIZE的值是什么。classA {…constintSIZE=100; //错误,企图在类声明中初始化const数据成员 intarray[SIZE]; //错误,未知的SIZE };const数据成员的初始化只能在类构造函数的初始化表中进行,例如classA {… A(intsize); //构造函数 constintSIZE; };A::A(intsize):SIZE(size) //构造函数的初始化表 { … } Aa(100); //对象a的SIZE值为100 Ab(200); //对象b的SIZE值为20018.8.2 函数设计规则函数是C++/C程序的基本功能单元,其重要性不言而喻。函数设计的细微缺点很容易导致该函数被错用,所以光使函数的功能正确是不够的。函数接口的两个要素是参数和返回值。C++语言中,函数的参数和返回值的传递方式有三种:值传递(passbyvalue)、指针传递(passbypointer)、引用传递(passbyreference)。 函数外部特性的注释规则函数外部特性注释必须在函数体上部采用中文说明,标准格式如下://输入参数:// 参数1: (指出参数的物理意义、量纲、取值范围等信息)// ……// 参数N:// ……//函数返回:// …… (指出返回值的物理意义、量纲、取值范围等信息)//功能描述:// ……//注意事项:// …… 参数规则[建议]对仅作为输入的参数,尽量使用const修饰符。如果输入参数以值传递的方式传递对象,则宜改用“const&”方式来传递,这样可以省去临时对象的构造和析构过程,从而提高效率。参数缺省值只能出现在函数的声明中,而不能出现在定义体中。例如: voidFoo(intx=0,inty=0); //正确,缺省值出现在函数的声明中 voidFoo(intx=0,inty=0) //错误,缺省值出现在函数的定义体中 { }如果函数有多个参数,参数只能从后向前挨个儿缺省,否则将导致函数调用语句怪模怪样。例如:voidFoo(intx,inty=0,intz=0); //正确voidFoo(intx=0,inty,intz=0); //错误【建议】避免函数有太多的参数,参数个数尽量控制在5个以内。如果参数太多,在使用时容易将参数类型或顺序搞错。【建议】尽量不要使用类型和数目不确定的参数。C标准库函数printf是采用不确定参数的典型代表,其原型为:

intprintf(constchat*format[,argument]…); 这种风格的函数在编译时丧失了严格的类型安全检查。【建议】非调度函数应减少或防止控制参数,尽量只使用数据参数。该规则可降低代码的控制耦合。 返回值的规则【建议】有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,可以附加返回值。【建议】如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率。而有些场合只能用“值传递”而不能用“引用传递”,否则会出错。【建议】函数要尽量只有一个返回点。不能返回引用和指针到局部对象。[说明]离开函数作用域时会销毁局部对象;使用销毁了的对象会造成灾难。不可返回由new初始化,之后又已解除引用的指针。[说明]由于支持链式表达式,造成返回对象不能删除,导致内存泄漏。 函数内部的实现规则[提示]在同一项目组应明确规定对接口函数参数的合法性检查应由函数的调用者负责还是由接口函数本身负责,缺省是由函数调用者负责。说明:对于模块间接口函数的参数的合法性检查这一问题,往往有两个极端现象,即:要么是调用者和被调用者对参数均不作合法性检查,结果就遗漏了合法性检查这一必要的处理过程,造成问题隐患;要么就是调用者和被调用者均对参数进行合法性检查,这种情况虽不会造成问题,但产生了冗余代码,降低了效率。(C++代码)使operator=检查自赋值。[说明]执行检查有两点重要的理由:首先,派生类对象的赋值涉及到调用每一个基类(在继承层次结构中位于此类的上方)的赋值操作符,跳过这些操作符就可以节省很多运行时间。其次,在复制“rvalue”对象前,赋值涉及到解构“lvalue”对象。在自赋值时,rvalue对象在赋值前就已销毁了,因此赋值的结果是不确定的。23.8.3 提高程序质量的技术8.3.1 内存管理规则BillGates在1981年曾经说过:640Koughttobeenoughforeverybody。程序员们经常编写内存管理程序,往往提心吊胆。同时,由于个人编程的习惯性缺陷,导致同类问题重复出现,如果不想触雷,唯一的解决办法就是发现所有潜伏的地雷并且排除它们。24.内存分配方式按照内存分配的位置不同,内存分配方式有三种:从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。25.常见的错误情况内存分配未成功,却使用了它。内存分配虽然成功,但是尚未初始化就引用它。内存分配成功并且已经初始化,但操作越过了内存的边界。忘记了释放内存,造成内存泄露。释放了内存却继续使用它。程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”。用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。26.注意事项不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。动态内存的申请与释放必须配对,防止内存泄漏。用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。malloc返回值的类型是void*,所以在调用malloc时要显式地进行类型转换,将void*转换成所需要的指针类型。用delete释放对象数组时,留意不要丢了符号。检查程序中内存申请/释放操作的成对性,防止内存泄漏。27.指针与数组的区别chara[]=“hello”;a[0]=‘X’;cout<<a<<endl;char*p=“world”;//注意p指向常量字符串p[0]=‘X’;//编译器不能发现该错误cout<<p<<endl;28.内存中指针参数的传递voidGetMemory(char*p,intnum){p=(char*)malloc(sizeof(char)*num);}voidTest(void){char*str=NULL;GetMemory(str,100);//str仍然为NULLstrcpy(str,"hello");//运行错误}29.示例用指向指针的指针申请动态内存如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,见示例。voidGetMemory2(char**p,intnum){*p=(char*)malloc(sizeof(char)*num);}voidTest2(void){char*str=NULL;GetMemory2(&str,100);//注意参数是&str,而不是strstrcpy(str,"hello");cout<<str<<endl;free(str);} 面向对象的设计规则比较面向对象程序设计和面向过程程序设计,还可以得到面向对象程序设计的其他优点:数据抽象的概念可以在保持外部接口不变的情况下改变内部实现,从而减少甚至避免对外界的干扰;通过继承大幅减少冗余的代码,并可以方便地扩展现有代码,提高编码效率,也减低了出错概率,降低软件维护的难度;结合面向对象分析、面向对象设计,允许将问题域中的对象直接映射到程序中,减少软件开发过程中中间环节的转换过程;通过对对象的辨别、划分可以将软件系统分割为若干相对为独立的部分,在一定程度上更便于控制软件复杂度;以对象为中心的设计可以帮助开发人员从静态(属性)和动态(方法)两个方面把握问题,从而更好地实现系统;通过对象的聚合、联合可以在保证封装与抽象的原则下实现对象在内在结构以及外在功能上的扩充,从而实现对象由低到高的升级。31.开-闭原则(Open-ClosedPrinciple,OCP)开-闭原则的定义及优点实现“开-闭”原则对可变性的封装原则32.里氏代换原则(LiskovSubstitutionPrinciple,LSP)里氏代换原则的定义定义:如果对每一个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都代换为O2时,程序P的行为没有变化,那么类型T2是类型T1的子类型。即,一个软件实体如果使用的是一个基类的话,那么一定适用于其子类。而且它觉察不出基类对象和子类对象的区别。也就是说,在软件里面,把基类都替换成它的子类,程序的行为没有变化。反过来的代换不成立,如果一个软件实体使用的是一个子类的话,那么它不一定适用于基类。任何基类可以出现的地方,子类一定可以出现。基于契约的设计、抽象出公共部分作为抽象基类的设计。里氏代换原则与“开-闭”原则的关系实现“开-闭”原则的关键步骤是抽象化。基类与子类之间的继承关系就是抽象化的体现。因此里氏代换原则是对实现抽象化的具体步骤的规范。违反里氏代换原则意味着违反了“开-闭”原则,反之未必。33.依赖倒转原则(dependenceinversionprinciple,DIP)接口实例/**InterfaceIManeuverableprovidesthespecificationforamaneuverablevehicle*/publicinterfaceIManeuverable{publicvoidleft();publicvoidright();publicvoidforward();publicvoidreverse();publicvoidclimb();publicvoiddive();publicvoidsetspeed(doublespeed);publicdoublegetspeed();

}publicclasscarimplementsIManeuverable{}publicclassBoardimplementsIManeuverable{}publicclassSubmarineimplementsIManeuverable{}34.讨论依赖倒转原则关系我们决定耦合的程度的依据何在呢?怎样将大系统拆分成小系统怎样做到依赖倒转?依赖倒转原则的优缺点35.合成/聚合复用原则(CARP)概念合成和聚合合成/聚合的优缺点继承来进行复用的优缺点36.迪米特法则(LawofDemeter,LoD)概述狭义的迪米特法则狭义的迪米特法则的缺点广义的迪米特法则广义迪米特法则在类的设计上的体现37.接口隔离原则(interfaceseparateprinciple,ISP)概念如何实现接口隔离原则不应该强迫用户依赖于他们不用的方法。利用委托分离接口。利用多继承分离接口。38.一些基本的设计模式AbstractFactory:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。Adapter:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。Bridge:将抽象部分与它的实现部分分离,使它们都可以独立地变化。Builder:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。ChainofResponsibility:为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。Command:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。39.Composite:将对象组合成树形结构以表示“部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。Decorator:动态地给一个对象添加一些额外的职责。就扩展功能而言,它比生成子类方式更为灵活。Facade:为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。FactoryMethod:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。FactoryMethod使一个类的实例化延迟到其子类。Flyweight:运用共享技术有效地支持大量细粒度的对象。Interpreter:给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。40.Iterator:提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。Mediator:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。Memento:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。Observer:定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。Prototype:用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。Proxy:为其他对象提供一个代理以控制对这个对象的访问。Singleton:保证一个类仅有一个实例,并提供一个访问它的全局访问点。41.State:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。Strategy:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。Template

Method:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。Visitor:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。42.8.4 代码审查8.4.1 代码审查的主要工作代码审查的主要工作:发现代码中的bug;从代码的易维护性、可扩展性角度考察代码的质量,提出修改建议。是否符合java开发规范和代码审核检查表代码编写者,代码审核者共同对代码的质量承担责任。这样才能保证CodeReview不是走过场,其中代码编写者承担主要责任,代码审核者承担次要责任。43.

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论