高质量编程演示文稿_第1页
高质量编程演示文稿_第2页
高质量编程演示文稿_第3页
高质量编程演示文稿_第4页
高质量编程演示文稿_第5页
已阅读5页,还剩92页未读 继续免费阅读

下载本文档

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

文档简介

高质量编程演示文稿1目前一页\总数九十七页\编于二十点高质量编程2目前二页\总数九十七页\编于二十点内容提要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 小结 目前三页\总数九十七页\编于二十点8.1 代码风格统一编程风格的意义很大,是一个优秀而且职业化的开发团队所必需的素质。增加开发过程代码的强壮性、可读性、易维护性。减少有经验和无经验开发人员编程所需的脑力工作,为软件的良好维护性打下好的基础。通过人为以及自动的方式对最终软件应用质量标准,使新的开发人员快速适应项目氛围。支持项目资源的复用:允许开发人员从一个项目区域移动到另一个,而不需要重新适应新的子项目团队的氛围。目前四页\总数九十七页\编于二十点8.1.1 程序的书写格式版本的声明格式////Copyright@2011,北京侏罗纪公司XX部//Allrightsreserved.////文件名称:filename.h//文件标识:见配置管理计划书//摘要:简要描述本文件的内容///当前版本:2.1//作者:输入作者名字//完成日期:2011年3月20日////取代版本:2.0//原作者:输入原作者名字//完成日期:2011年2月10日//目前五页\总数九十七页\编于二十点头文件的书写格式头文件必须包含下列内容:头文件开头处的版权和版本声明。预处理块。函数和类结构声明等。//版权和版本声明见示例1-1。#ifndefGRAPHICS_H//防止graphics.h被重复引用#defineGRAPHICS_H#include<math.h>//引用标准库的头文件

…#include“myheader.h”//引用非标准库的头文件

…voidFunction1(…);//全局函数声明

…classBox //类结构声明{ …};#endif目前六页\总数九十七页\编于二十点定义文件的书写格式定义文件的书写格式:必须包含三部分内容:定义文件开头处的版权和版本声明;对一些头文件的引用;程序的实现体(包括数据和代码)。//版权和版本声明见示例1-1,此处省略。#include“graphics.h” //引用头文件

…//全局函数的实现体voidFunction1(…){ …}//类成员函数的实现体voidDraw(…){ …}目前七页\总数九十七页\编于二十点空行的使用//空行//空行voidFunction1(…){ …}//空行//空行voidFunction2(…){ …}//空行//空行voidFunction3(…){ …}//空行While(condition){ statement1;

//空行

if(condition) { statement2;

} else { statement3;

}//空行

statement4;}目前八页\总数九十七页\编于二十点

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

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

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

++stat_poi;*stat_poi+=1;//此二语句功能相当于“*++stat_poi+=1;”目前十四页\总数九十七页\编于二十点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)不可将浮点变量用“==”或“!=”与任何数字比较。目前十五页\总数九十七页\编于二十点循环语句的效率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];}}目前十六页\总数九十七页\编于二十点建议【建议】如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。例如,下面代码: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();}目前十七页\总数九十七页\编于二十点建议【建议】for语句的循环控制变量的取值采用“半开半闭区间”写法。例如for(x=0;x<N;x++){ …}for(x=0;x<=N-1;x++){ …}目前十八页\总数九十七页\编于二十点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值为200目前十九页\总数九十七页\编于二十点8.2 函数设计规则函数是C++/C程序的基本功能单元,其重要性不言而喻。函数设计的细微缺点很容易导致该函数被错用,所以光使函数的功能正确是不够的。函数接口的两个要素是参数和返回值。C++语言中,函数的参数和返回值的传递方式有三种:值传递(passbyvalue)、指针传递(passbypointer)、引用传递(passbyreference)。目前二十页\总数九十七页\编于二十点8.2.1 函数外部特性的注释规则函数外部特性注释必须在函数体上部采用中文说明,标准格式如下://输入参数:// 参数1: (指出参数的物理意义、量纲、取值范围等信息)// ……// 参数N:// ……//函数返回:// …… (指出返回值的物理意义、量纲、取值范围等信息)//功能描述:// ……//注意事项:// ……目前二十一页\总数九十七页\编于二十点8.2.2 参数规则[建议]对仅作为输入的参数,尽量使用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]…); 这种风格的函数在编译时丧失了严格的类型安全检查。【建议】非调度函数应减少或防止控制参数,尽量只使用数据参数。该规则可降低代码的控制耦合。目前二十二页\总数九十七页\编于二十点8.2.3 返回值的规则【建议】有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,可以附加返回值。【建议】如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率。而有些场合只能用“值传递”而不能用“引用传递”,否则会出错。【建议】函数要尽量只有一个返回点。不能返回引用和指针到局部对象。[说明]离开函数作用域时会销毁局部对象;使用销毁了的对象会造成灾难。不可返回由new初始化,之后又已解除引用的指针。[说明]由于支持链式表达式,造成返回对象不能删除,导致内存泄漏。目前二十三页\总数九十七页\编于二十点8.2.4 函数内部的实现规则[提示]在同一项目组应明确规定对接口函数参数的合法性检查应由函数的调用者负责还是由接口函数本身负责,缺省是由函数调用者负责。说明:对于模块间接口函数的参数的合法性检查这一问题,往往有两个极端现象,即:要么是调用者和被调用者对参数均不作合法性检查,结果就遗漏了合法性检查这一必要的处理过程,造成问题隐患;要么就是调用者和被调用者均对参数进行合法性检查,这种情况虽不会造成问题,但产生了冗余代码,降低了效率。(C++代码)使operator=检查自赋值。[说明]执行检查有两点重要的理由:首先,派生类对象的赋值涉及到调用每一个基类(在继承层次结构中位于此类的上方)的赋值操作符,跳过这些操作符就可以节省很多运行时间。其次,在复制“rvalue”对象前,赋值涉及到解构“lvalue”对象。在自赋值时,rvalue对象在赋值前就已销毁了,因此赋值的结果是不确定的。目前二十四页\总数九十七页\编于二十点8.3 提高程序质量的技术8.3.1 内存管理规则BillGates在1981年曾经说过:640Koughttobeenoughforeverybody。程序员们经常编写内存管理程序,往往提心吊胆。同时,由于个人编程的习惯性缺陷,导致同类问题重复出现,如果不想触雷,唯一的解决办法就是发现所有潜伏的地雷并且排除它们。目前二十五页\总数九十七页\编于二十点内存分配方式按照内存分配的位置不同,内存分配方式有三种:从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。目前二十六页\总数九十七页\编于二十点常见的错误情况内存分配未成功,却使用了它。内存分配虽然成功,但是尚未初始化就引用它。内存分配成功并且已经初始化,但操作越过了内存的边界。忘记了释放内存,造成内存泄露。释放了内存却继续使用它。程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”。用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。目前二十七页\总数九十七页\编于二十点注意事项不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。动态内存的申请与释放必须配对,防止内存泄漏。用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。malloc返回值的类型是void*,所以在调用malloc时要显式地进行类型转换,将void*转换成所需要的指针类型。用delete释放对象数组时,留意不要丢了符号。检查程序中内存申请/释放操作的成对性,防止内存泄漏。目前二十八页\总数九十七页\编于二十点指针与数组的区别chara[]=“hello”;a[0]=‘X’;cout<<a<<endl;char*p=“world”;//注意p指向常量字符串p[0]=‘X’;//编译器不能发现该错误cout<<p<<endl;目前二十九页\总数九十七页\编于二十点内存中指针参数的传递voidGetMemory(char*p,intnum){p=(char*)malloc(sizeof(char)*num);}voidTest(void){char*str=NULL;GetMemory(str,100);//str仍然为NULLstrcpy(str,"hello");//运行错误}目前三十页\总数九十七页\编于二十点示例用指向指针的指针申请动态内存如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,见示例。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);}目前三十一页\总数九十七页\编于二十点8.3.2 面向对象的设计规则比较面向对象程序设计和面向过程程序设计,还可以得到面向对象程序设计的其他优点:数据抽象的概念可以在保持外部接口不变的情况下改变内部实现,从而减少甚至避免对外界的干扰;通过继承大幅减少冗余的代码,并可以方便地扩展现有代码,提高编码效率,也减低了出错概率,降低软件维护的难度;结合面向对象分析、面向对象设计,允许将问题域中的对象直接映射到程序中,减少软件开发过程中中间环节的转换过程;通过对对象的辨别、划分可以将软件系统分割为若干相对为独立的部分,在一定程度上更便于控制软件复杂度;以对象为中心的设计可以帮助开发人员从静态(属性)和动态(方法)两个方面把握问题,从而更好地实现系统;通过对象的聚合、联合可以在保证封装与抽象的原则下实现对象在内在结构以及外在功能上的扩充,从而实现对象由低到高的升级。目前三十二页\总数九十七页\编于二十点面向对象设计原则概述软件的可维护性和可复用性知名软件大师RobertC.Martin认为一个可维护性较低的软件设计,通常由于如下四个原因造成:过于僵硬(Rigidity)过于脆弱(Fragility)复用率低(Immobility)黏度过高(Viscosity)RobertC.Martin目前三十三页\总数九十七页\编于二十点面向对象设计原则概述软件的可维护性和可复用性软件工程和建模大师PeterCoad认为,一个好的系统设计应该具备如下三个性质:可扩展性(Extensibility)灵活性(Flexibility)可插入性(Pluggability)PeterCoad目前三十四页\总数九十七页\编于二十点面向对象设计原则概述软件的可维护性和可复用性

软件的复用(Reuse)或重用拥有众多优点,如可以提高软件的开发效率,提高软件质量,节约开发成本,恰当的复用还可以改善系统的可维护性。面向对象设计复用的目标在于实现支持可维护性的复用。

在面向对象的设计里面,可维护性复用都是以面向对象设计原则为基础的,这些设计原则首先都是复用的原则,遵循这些设计原则可以有效地提高系统的复用性,同时提高系统的可维护性。目前三十五页\总数九十七页\编于二十点面向对象设计原则概述软件的可维护性和可复用性面向对象设计原则和设计模式也是对系统进行合理重构的指南针,重构(Refactoring)是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。

MartinFowler目前三十六页\总数九十七页\编于二十点面向对象设计原则概述面向对象设计原则简介常用的面向对象设计原则包括七个,这些原则并不是孤立存在的,它们相互依赖,相互补充。设计原则名称设计原则简介重要性单一职责原则(SingleResponsibilityPrinciple,SRP)类的职责要单一,不能将太多的职责放在一个类中。★★★★☆开闭原则(Open-ClosedPrinciple,OCP)软件实体对扩展是开放的,但对修改是关闭的,即在不修改一个软件实体的基础上去扩展其功能。★★★★★里氏代换原则(LiskovSubstitutionPrinciple,LSP)在软件系统中,一个可以接受基类对象的地方必然可以接受一个子类对象。★★★★☆依赖倒转原则(DependencyInversionPrinciple,DIP)要针对抽象层编程,而不要针对具体类编程。★★★★★接口隔离原则(InterfaceSegregationPrinciple,ISP)使用多个专门的接口来取代一个统一的接口。★★☆☆☆合成复用原则(CompositeReusePrinciple,CRP)在系统中应该尽量多使用组合和聚合关联关系,尽量少使用甚至不使用继承关系。★★★★☆迪米特法则(LawofDemeter,LoD)一个软件实体对其他实体的引用越少越好,或者说如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,而是通过引入一个第三者发生间接交互。★★★☆☆目前三十七页\总数九十七页\编于二十点单一职责原则单一职责原则定义单一职责原则(SingleResponsibilityPrinciple,SRP)定义如下:在软件系统中,一个类只负责一个功能领域中的相应职责。另一种定义方式如下:就一个类而言,应该仅有一个引起它变化的原因。

目前三十八页\总数九十七页\编于二十点单一职责原则单一职责原则分析一个类(或者大到模块,小到方法)承担的职责越多,它被复用的可能性越小。而且如果一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作。类的职责主要包括两个方面:数据职责和行为职责,数据职责通过其属性来体现,而行为职责通过其方法来体现。单一职责原则是实现高内聚、低耦合的指导方针,在很多代码重构手法中都能找到它的存在,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。目前三十九页\总数九十七页\编于二十点单一职责原则单一职责原则实例实例说明某基于Java的C/S系统的“登录功能”通过如下登录类(Login)实现:现使用单一职责原则对其进行重构。目前四十页\总数九十七页\编于二十点单一职责原则单一职责原则实例实例解析使用单一职责原则重构后的类图:目前四十一页\总数九十七页\编于二十点开闭原则开闭原则定义开闭原则(Open-ClosedPrinciple,OCP)定义如下:一个软件实体应当对扩展开放,对修改关闭。也就是说在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,即实现在不修改源代码的情况下改变这个模块的行为。目前四十二页\总数九十七页\编于二十点开闭原则开闭原则分析开闭原则由BertrandMeyer于1988年提出,它是面向对象设计中最重要的原则之一。在开闭原则的定义中,软件实体可以指一个软件模块、一个由多个类组成的局部结构或一个独立的类。目前四十三页\总数九十七页\编于二十点开闭原则开闭原则分析抽象化是开闭原则的关键。绝大部分的设计模式都符合开闭原则,在对每一个模式进行优缺点评价时都会以开闭原则作为一个重要的评价依据,以判断基于该模式设计的系统是否具备良好的灵活性和可扩展性。目前四十四页\总数九十七页\编于二十点开闭原则开闭原则实例

实例说明某图形界面系统提供了各种不同形状的按钮,客户端代码可针对这些按钮进行编程,用户可能会改变需求要求使用不同的按钮,原始设计方案如图所示:现对该系统进行重构,使之满足开闭原则的要求。目前四十五页\总数九十七页\编于二十点开闭原则开闭原则实例实例解析

目前四十六页\总数九十七页\编于二十点里氏代换原则里氏代换原则定义里氏代换原则(LiskovSubstitutionPrinciple,LSP)有两种定义方式,第一种定义方式相对严格,其定义如下:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有变化,那么类型S是类型T的子类型。第二种更容易理解的定义方式如下:所有引用基类(父类)的地方必须能透明地使用其子类的对象。目前四十七页\总数九十七页\编于二十点里氏代换原则里氏代换原则分析里氏代换原则由2008年图灵奖得主、美国第一位计算机科学女博士、麻省理工学院BarbaraLiskov教授和卡内基-梅隆大学JeannetteWing教授于1994年提出。芭芭拉·利斯科夫(BarbaraLiskov),美国计算机科学家,2008年图灵奖(计算机领域的诺贝尔奖)得主,2004年约翰·冯诺依曼奖得主。美国工程院院士,美国艺术与科学院院士,美国计算机协会会士。现任麻省理工学院电子电气与计算机科学系教授。她是美国第一个计算机科学女博士,第二位获得图灵奖的女科学家。目前四十八页\总数九十七页\编于二十点里氏代换原则里氏代换原则分析里氏代换原则可以通俗表述为:在软件中如果能够使用基类对象,那么一定能够使用其子类对象。把基类都替换成它的子类,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类的话,那么它不一定能够使用基类。里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。目前四十九页\总数九十七页\编于二十点里氏代换原则里氏代换原则分析喜欢动物喜欢猫因为猫是动物目前五十页\总数九十七页\编于二十点里氏代换原则里氏代换原则实例实例说明某系统需要实现对重要数据(如用户密码)的加密处理,在数据操作类(DataOperator)中需要调用加密类中定义的加密算法,系统提供了两个不同的加密类,CipherA和CipherB,它们实现不同的加密方法,在DataOperator中可以选择其中的一个实现加密操作。如图所示:目前五十一页\总数九十七页\编于二十点里氏代换原则里氏代换原则实例实例说明如果需要更换一个加密算法类或者增加并使用一个新的加密算法类,如将CipherA改为CipherB,则需要修改客户类Client和数据操作类DataOperator的源代码,违背了开闭原则。现使用里氏代换原则对其进行重构,使得系统可以灵活扩展,符合开闭原则。目前五十二页\总数九十七页\编于二十点里氏代换原则里氏代换原则实例实例解析目前五十三页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则定义依赖倒转原则(DependenceInversionPrinciple,DIP)的定义如下:高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。另一种表述为:要针对接口编程,不要针对实现编程。目前五十四页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则分析依赖倒转原则是RobertC.Martin在1996年为《C++Reporter》所写的专栏EngineeringNotebook的第三篇,后来加入到他在2002年出版的经典著作《AgileSoftwareDevelopment,Principles,Patterns,andPractices》中。目前五十五页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则分析简单来说,依赖倒转原则就是指:代码要依赖于抽象的类,而不要依赖于具体的类;要针对接口或抽象类编程,而不是针对具体类编程。实现开闭原则的关键是抽象化,并且从抽象化导出具体化实现,如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要手段。目前五十六页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则分析依赖倒转原则的常用实现方式之一是在代码中使用抽象类,而将具体类放在配置文件中。“将抽象放进代码,将细节放进元数据”《程序员修炼之道:从小工到专家》目前五十七页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则分析类之间的耦合零耦合关系具体耦合关系抽象耦合关系依赖倒转原则要求客户端依赖于抽象耦合,以抽象方式耦合是依赖倒转原则的关键。目前五十八页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则分析依赖注入

目前五十九页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则分析依赖注入

构造注入(ConstructorInjection):通过构造函数注入实例变量。设值注入(SetterInjection):通过Setter方法注入实例变量。接口注入(InterfaceInjection):通过接口方法注入实例变量。

目前六十页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则分析构造注入publicinterfaceAbstractBook{publicvoidview();}publicinterfaceAbstractReader{publicvoidread();}publicclassConcreteBookimplementsAbstractBook{ publicvoidview() { …… }}publicclassConcreteReaderimplementsAbstractReader{ privateAbstractBookbook; publicConcreteReader(AbstractBookbook) { this.book=book; } publicvoidread() { book.view(); }}目前六十一页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则分析设值注入publicinterfaceAbstractBook{publicvoidview();}publicinterfaceAbstractReader{publicvoidsetBook(AbstractBookbook);publicvoidread();}publicclassConcreteBookimplementsAbstractBook{ publicvoidview() { ...... }}publicclassConcreteReaderimplementsAbstractReader{ privateAbstractBookbook; publicvoidsetBook(AbstractBookbook) { this.book=book; } publicvoidread() { book.view(); }}目前六十二页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则分析接口注入publicinterfaceAbstractBook{publicvoidview();}publicinterfaceAbstractReader{publicvoidread(AbstractBookbook);}publicclassConcreteBookimplementsAbstractBook{ publicvoidview() { ...... }}publicclassConcreteReaderimplementsAbstractReader{ publicvoidread(AbstractBookbook) { book.view(); }}目前六十三页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则实例实例说明某系统提供一个数据转换模块,可以将来自不同数据源的数据转换成多种格式,如可以转换来自数据库的数据(DatabaseSource)、也可以转换来自文本文件的数据(TextSource),转换后的格式可以是xml文件(XMLTransformer)、也可以是xls文件(XLSTransformer)等。目前六十四页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则实例实例说明由于需求的变化,该系统可能需要增加新的数据源或者新的文件格式,每增加一个新的类型的数据源或者新的类型的文件格式,客户类MainClass都需要修改源代码,以便使用新的类,违背了开闭原则。现使用依赖倒转原则对其进行重构。目前六十五页\总数九十七页\编于二十点依赖倒转原则依赖倒转原则实例实例解析目前六十六页\总数九十七页\编于二十点接口隔离原则接口隔离原则定义接口隔离原则(InterfaceSegregationPrinciple,ISP)的定义如下:客户端不应该依赖那些它不需要的接口。注意,在该定义中的接口指的是所定义的方法。另一种定义方法如下:一旦一个接口太大,则需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。目前六十七页\总数九十七页\编于二十点接口隔离原则接口隔离原则分析

接口隔离原则是指使用多个专门的接口,而不使用单一的总接口。每一个接口应该承担一种相对独立的角色,不多不少,不干不该干的事,该干的事都要干。(1)一个接口就只代表一个角色,每个角色都有它特定的一个接口,此时这个原则可以叫做“角色隔离原则”。(2)接口仅仅提供客户端需要的行为,即所需的方法,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口。目前六十八页\总数九十七页\编于二十点接口隔离原则接口隔离原则分析使用接口隔离原则拆分接口时,首先必须满足单一职责原则,将一组相关的操作定义在一个接口中,且在满足高内聚的前提下,接口中的方法越少越好。可以在进行系统设计时采用定制服务的方式,即为不同的客户端提供宽窄不同的接口,只提供用户需要的行为,而隐藏用户不需要的行为。目前六十九页\总数九十七页\编于二十点接口隔离原则接口隔离原则实例实例说明下图展示了一个拥有多个客户类的系统,在系统中定义了一个巨大的接口AbstractService来服务所有的客户类。可以使用接口隔离原则对其进行重构。目前七十页\总数九十七页\编于二十点接口隔离原则接口隔离原则实例实例解析目前七十一页\总数九十七页\编于二十点合成复用原则合成复用原则定义合成复用原则(CompositeReusePrinciple,CRP)又称为组合/聚合复用原则(Composition/AggregateReusePrinciple,CARP),其定义如下:尽量使用对象组合,而不是继承来达到复用的目的。目前七十二页\总数九十七页\编于二十点合成复用原则合成复用原则分析合成复用原则就是指在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用其已有功能的目的。简言之:要尽量使用组合/聚合关系,少用继承。目前七十三页\总数九十七页\编于二十点合成复用原则合成复用原则分析在面向对象设计中,可以通过两种基本方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承。继承复用:实现简单,易于扩展。破坏系统的封装性,从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;只能在有限的环境中使用。(“白箱”复用)组合/聚合复用:耦合度相对较低,选择性地调用成员对象的操作;可以在运行时动态进行。(“黑箱”复用)目前七十四页\总数九十七页\编于二十点合成复用原则合成复用原则分析组合/聚合可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。目前七十五页\总数九十七页\编于二十点合成复用原则合成复用原则实例实例说明某教学管理系统部分数据库访问类设计如图所示:目前七十六页\总数九十七页\编于二十点合成复用原则合成复用原则实例实例说明如果需要更换数据库连接方式,如原来采用JDBC连接数据库,现在采用数据库连接池连接,则需要修改DBUtil类源代码。如果StudentDAO采用JDBC连接,但是TeacherDAO采用连接池连接,则需要增加一个新的DBUtil类,并修改StudentDAO或TeacherDAO的源代码,使之继承新的数据库连接类,这将违背开闭原则,系统扩展性较差。现使用合成复用原则对其进行重构。目前七十七页\总数九十七页\编于二十点合成复用原则合成复用原则实例实例解析

目前七十八页\总数九十七页\编于二十点迪米特法则迪米特法则定义迪米特法则(LawofDemeter,LoD)又称为最少知识原则(LeastKnowledgePrinciple,LKP),它有多种定义方法,其中几种典型定义如下:(1)不要和“陌生人”说话。(2)只与你的直接朋友通信。(3)每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。目前七十九页\总数九十七页\编于二十点迪米特法则迪米特法则分析迪米特法则来自于1987年秋美国东北大学(NortheasternUniversity)一个名为“Demeter”的研究项目。简单来说,迪米特法则就是指一个软件实体应当尽可能少的与其他实体发生相互作用。这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。目前八十页\总数九十七页\编于二十点迪米特法则迪米特法则分析在迪米特法则中,对于一个对象,其朋友包括以下几类:(1)当前对象本身(this);(2)以参数形式传入到当前对象方法中的对象;(3)当前对象的成员对象;(4)如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;(5)当前对象所创建的对象。任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生人”。目前八十一页\总数九十七页\编于二十点迪米特法则迪米特法则分析迪米特法则可分为狭义法则和广义法则。在狭义的迪米特法则中,如果两个类之间不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。目前八十二页\总数九十七页\编于二十点迪米特法则迪米特法则分析狭义的迪米特法则:可以降低类之间的耦合,但是会在系统中增加大量的小方法并散落在系统的各个角落,它可以使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联,但是也会造成系统的不同模块之间的通信效率降低,使得系统的不同模块之间不容易协调。广义的迪米特法则:指对对象之间的信息流量、流向以及信息的影响的控制,主要是对信息隐藏的控制。信息的隐藏可以使各个子系统之间脱耦,从而允许它们独立地被开发、优化、使用和修改,同时可以促进软件的复用,由于每一个模块都不依赖于其他模块而存在,因此每一个模块都可以独立地在其他的地方使用。一个系统的规模越大,信息的隐藏就越重要,而信息隐藏的重要性也就越明显。目前八十三页\总数九十七页\编于二十点迪米特法则迪米特法则分析迪米特法则的主要用途在于控制信息的过载:在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及;在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;在类的设计上,只要有可能,一个类型应当设计成不变类;在对其他类的引用上,一个对象对其他对象的引用应当降到最低。目前八十四页\总数九十七页\编于二十点迪米特法则迪米特法则实例实例说明某系统界面类(如Form1、Form2等类)与数据访问类(如DAO1、DAO2等类)之间的调用关系较为复杂,如图所示:目前八十五页\总数九十七页\编于二十点迪米特法则迪米特法则实例实例解析目前八十六页\总数九十七页\编于二十点小结对于面向对象的软件系统设计来说,在支持可维护性的同时,需要提高系统的可复用性。软件的复用可以提高软件的开发效率,提高软件质量,节约开发成本,恰当的复用还可以改善系统的可维护性。单一职责原则要求在软件系统中,一个类只负责一个功能领域中的相应职责。开闭原则要求一个软件实体应当对扩展开放,对修改关闭,即在不修改源代码的基础上扩展一个系统的行为。里氏代换原则可以通俗表述为在软件中如果能够使用基类对象,那么一定能够使用其子类对象。目前八十七页\总数九十七页\编于二十点小结依赖倒转原则要求抽象不应该依赖于细节,细节应该依赖于抽象;要针对接口编程,不要针对实现编程。接口隔离原则要求客户端不应该依赖那些它不需要的接口,即将一些大的接口细化成一些小的接口供客户端使用。合成复用原则要求复用时尽量使用对象组合,而不使用继承。迪米特法则要求一个软件实体应当尽可能少的与其他实体发生相互作用。目前八十八页\总数九十七页\编于二十点一些基本的设计模式AbstractFactory:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。Adapter:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。Bridge:将抽象部分与它的实现部分分离,使它们都可以独立地变化。Builder:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。ChainofResponsibility:为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。Command:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。目前八十九页\总数九十七页\编于二十点Composite:将对象组合成树形结构以表示“部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。Decorator:动态地给一个对象添加一些额外的职责。就扩展功能而言,它比生成子

温馨提示

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

评论

0/150

提交评论