高質量編程演示文稿_第1頁
高質量編程演示文稿_第2頁
高質量編程演示文稿_第3頁
高質量編程演示文稿_第4頁
高質量編程演示文稿_第5頁
已閱讀5頁,還剩92頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

高質量編程演示文稿1第一頁,共九十七頁。高質量編程2第二頁,共九十七頁。內容提要8.1 代碼風格 8.1.1 程序的書寫格式 8.1.2 Windows程序命名規(guī)則8.1.3 共性規(guī)則8.1.4 表達式和基本語句 8.2 函數設計規(guī)則 8.2.1 函數外部特性的注釋規(guī)則 8.2.2 參數規(guī)則 8.2.3 返回值的規(guī)則 8.2.4 函數內部的實現規(guī)則 8.3 提高程序質量的技術 8.3.1 內存管理規(guī)則 8.3.2 面向對象的設計規(guī)則 8.4 代碼審查 8.4.1 代碼審查的主要工作 8.4.2 代碼審查的流程 8.4.3 Java代碼審查的常見錯誤 8.5 小結 第三頁,共九十七頁。8.1 代碼風格統(tǒng)一編程風格的意義很大,是一個優(yōu)秀而且職業(yè)化的開發(fā)團隊所必需的素質。增加開發(fā)過程代碼的強壯性、可讀性、易維護性。減少有經驗和無經驗開發(fā)人員編程所需的腦力工作,為軟件的良好維護性打下好的基礎。通過人為以及自動的方式對最終軟件應用質量標準,使新的開發(fā)人員快速適應項目氛圍。支持項目資源的復用:允許開發(fā)人員從一個項目區(qū)域移動到另一個,而不需要重新適應新的子項目團隊的氛圍。第四頁,共九十七頁。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程序命名規(guī)則匈牙利命名法是一種編程時的命名規(guī)范?;驹瓌t是:變量名=屬性+類型+對象描述,其中每一對象的名稱都要求有明確含義,可以取對象名字全稱或名字的一部分。命名要基于容易記憶容易理解的原則。保證名字的連貫性是非常重要的。舉例來說,表單的名稱為form,那么在匈牙利命名法中可以簡寫為frm,則當表單變量名稱為Switchboard時,變量全稱應該為frmSwitchboard。這樣可以很容易從變量名看出Switchboard是一個表單,同樣,如果此變量類型為標簽,那么就應命名成lblSwitchboard??梢钥闯?,匈牙利命名法非常便于記憶,而且使變量名非常清晰易懂,這樣,增強了代碼的可讀性,方便各程序員之間相互交流代碼。據說這種命名法是一位叫CharlesSimonyi的匈牙利程序員發(fā)明的,后來他在微軟呆了幾年,于是這種命名法就通過微軟的各種產品和文檔資料向世界傳播開了?,F在,大部分程序員不管自己使用什么軟件進行開發(fā),或多或少都使用了這種命名法。第十頁,共九十七頁。常用的數據類型前綴前綴類型例子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 共性規(guī)則[提示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;//因為不同操作系統(tǒng)的TRUE和FALSE不一樣,如WINDOWS里TRUE是1而有些系統(tǒng)//TRUE是0cyj應當將整型變量用“==”或“!=”直接與0比較。假設整型變量的名字為value,它與零值比較的標準if語句如下:if(value==0)if(value!=0)不可模仿布爾變量的風格而寫成if(value) //會讓人誤解value是布爾變量if(!value)不可將浮點變量用“==”或“!=”與任何數字比較。第十五頁,共九十七頁。循環(huán)語句的效率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];}}第十六頁,共九十七頁。建議【建議】如果循環(huán)體內存在邏輯判斷,并且循環(huán)次數很大,宜將邏輯判斷移到循環(huán)體的外面。例如,下面代碼: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語句的循環(huán)控制變量的取值采用“半開半閉區(qū)間”寫法。例如for(x=0;x<N;x++){ …}for(x=0;x<=N-1;x++){ …}第十八頁,共九十七頁。C++類中的常量不能在類聲明中初始化const數據成員。以下用法是錯誤的,因為類的對象未被創(chuàng)建時,編譯器不知道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 函數設計規(guī)則函數是C++/C程序的基本功能單元,其重要性不言而喻。函數設計的細微缺點很容易導致該函數被錯用,所以光使函數的功能正確是不夠的。函數接口的兩個要素是參數和返回值。C++語言中,函數的參數和返回值的傳遞方式有三種:值傳遞(passbyvalue)、指針傳遞(passbypointer)、引用傳遞(passbyreference)。第二十頁,共九十七頁。8.2.1 函數外部特性的注釋規(guī)則函數外部特性注釋必須在函數體上部采用中文說明,標準格式如下://輸入參數:// 參數1: (指出參數的物理意義、量綱、取值范圍等信息)// ……// 參數N:// ……//函數返回:// …… (指出返回值的物理意義、量綱、取值范圍等信息)//功能描述:// ……//注意事項:// ……第二十一頁,共九十七頁。8.2.2 參數規(guī)則[建議]對僅作為輸入的參數,盡量使用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個以內。如果參數太多,在使用時容易將參數類型或順序搞錯?!窘ㄗh】盡量不要使用類型和數目不確定的參數。C標準庫函數printf是采用不確定參數的典型代表,其原型為:

intprintf(constchat*format[,argument]…); 這種風格的函數在編譯時喪失了嚴格的類型安全檢查?!窘ㄗh】非調度函數應減少或防止控制參數,盡量只使用數據參數。該規(guī)則可降低代碼的控制耦合。第二十二頁,共九十七頁。8.2.3 返回值的規(guī)則【建議】有時候函數原本不需要返回值,但為了增加靈活性如支持鏈式表達,可以附加返回值。【建議】如果函數的返回值是一個對象,有些場合用“引用傳遞”替換“值傳遞”可以提高效率。而有些場合只能用“值傳遞”而不能用“引用傳遞”,否則會出錯。【建議】函數要盡量只有一個返回點。不能返回引用和指針到局部對象。[說明]離開函數作用域時會銷毀局部對象;使用銷毀了的對象會造成災難。不可返回由new初始化,之后又已解除引用的指針。[說明]由于支持鏈式表達式,造成返回對象不能刪除,導致內存泄漏。第二十三頁,共九十七頁。8.2.4 函數內部的實現規(guī)則[提示]在同一項目組應明確規(guī)定對接口函數參數的合法性檢查應由函數的調用者負責還是由接口函數本身負責,缺省是由函數調用者負責。說明:對于模塊間接口函數的參數的合法性檢查這一問題,往往有兩個極端現象,即:要么是調用者和被調用者對參數均不作合法性檢查,結果就遺漏了合法性檢查這一必要的處理過程,造成問題隱患;要么就是調用者和被調用者均對參數進行合法性檢查,這種情況雖不會造成問題,但產生了冗余代碼,降低了效率。(C++代碼)使operator=檢查自賦值。[說明]執(zhí)行檢查有兩點重要的理由:首先,派生類對象的賦值涉及到調用每一個基類(在繼承層次結構中位于此類的上方)的賦值操作符,跳過這些操作符就可以節(jié)省很多運行時間。其次,在復制“rvalue”對象前,賦值涉及到解構“l(fā)value”對象。在自賦值時,rvalue對象在賦值前就已銷毀了,因此賦值的結果是不確定的。第二十四頁,共九十七頁。8.3 提高程序質量的技術8.3.1 內存管理規(guī)則BillGates在1981年曾經說過:640Koughttobeenoughforeverybody。程序員們經常編寫內存管理程序,往往提心吊膽。同時,由于個人編程的習慣性缺陷,導致同類問題重復出現,如果不想觸雷,唯一的解決辦法就是發(fā)現所有潛伏的地雷并且排除它們。第二十五頁,共九十七頁。內存分配方式按照內存分配的位置不同,內存分配方式有三種:從靜態(tài)存儲區(qū)域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static變量。在棧上創(chuàng)建。在執(zhí)行函數時,函數內局部變量的存儲單元都可以在棧上創(chuàng)建,函數執(zhí)行結束時這些存儲單元自動被釋放。棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。從堆上分配,亦稱動態(tài)內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員自己負責在何時用free或delete釋放內存。動態(tài)內存的生存期由我們決定,使用非常靈活,但問題也最多。第二十六頁,共九十七頁。常見的錯誤情況內存分配未成功,卻使用了它。內存分配雖然成功,但是尚未初始化就引用它。內存分配成功并且已經初始化,但操作越過了內存的邊界。忘記了釋放內存,造成內存泄露。釋放了內存卻繼續(xù)使用它。程序中的對象調用關系過于復雜,實在難以搞清楚某個對象究竟是否已經釋放了內存,此時應該重新設計數據結構,從根本上解決對象管理的混亂局面。函數的return語句寫錯了,注意不要返回指向“棧內存”的“指針”或者“引用”,因為該內存在函數體結束時被自動銷毀。使用free或delete釋放了內存后,沒有將指針設置為NULL。導致產生“野指針”。用malloc或new申請內存之后,應該立即檢查指針值是否為NULL。防止使用指針值為NULL的內存。第二十七頁,共九十七頁。注意事項不要忘記為數組和動態(tài)內存賦初值。防止將未被初始化的內存作為右值使用。避免數組或指針的下標越界,特別要當心發(fā)生“多1”或者“少1”操作。動態(tài)內存的申請與釋放必須配對,防止內存泄漏。用free或delete釋放了內存之后,立即將指針設置為NULL,防止產生“野指針”。malloc返回值的類型是void*,所以在調用malloc時要顯式地進行類型轉換,將void*轉換成所需要的指針類型。用delete釋放對象數組時,留意不要丟了符號。檢查程序中內存申請/釋放操作的成對性,防止內存泄漏。第二十八頁,共九十七頁。指針與數組的區(qū)別chara[]=“hello”;a[0]=‘X’;cout<<a<<endl;char*p=“world”;//注意p指向常量字符串p[0]=‘X’;//編譯器不能發(fā)現該錯誤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");//運行錯誤}第三十頁,共九十七頁。示例用指向指針的指針申請動態(tài)內存如果非得要用指針參數去申請內存,那么應該改用“指向指針的指針”,見示例。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 面向對象的設計規(guī)則比較面向對象程序設計和面向過程程序設計,還可以得到面向對象程序設計的其他優(yōu)點:數據抽象的概念可以在保持外部接口不變的情況下改變內部實現,從而減少甚至避免對外界的干擾;通過繼承大幅減少冗余的代碼,并可以方便地擴展現有代碼,提高編碼效率,也減低了出錯概率,降低軟件維護的難度;結合面向對象分析、面向對象設計,允許將問題域中的對象直接映射到程序中,減少軟件開發(fā)過程中中間環(huán)節(jié)的轉換過程;通過對對象的辨別、劃分可以將軟件系統(tǒng)分割為若干相對為獨立的部分,在一定程度上更便于控制軟件復雜度;以對象為中心的設計可以幫助開發(fā)人員從靜態(tài)(屬性)和動態(tài)(方法)兩個方面把握問題,從而更好地實現系統(tǒng);通過對象的聚合、聯合可以在保證封裝與抽象的原則下實現對象在內在結構以及外在功能上的擴充,從而實現對象由低到高的升級。第三十二頁,共九十七頁。面向對象設計原則概述軟件的可維護性和可復用性知名軟件大師RobertC.Martin認為一個可維護性較低的軟件設計,通常由于如下四個原因造成:過于僵硬(Rigidity)過于脆弱(Fragility)復用率低(Immobility)黏度過高(Viscosity)RobertC.Martin第三十三頁,共九十七頁。面向對象設計原則概述軟件的可維護性和可復用性軟件工程和建模大師PeterCoad認為,一個好的系統(tǒng)設計應該具備如下三個性質:可擴展性(Extensibility)靈活性(Flexibility)可插入性(Pluggability)

PeterCoad第三十四頁,共九十七頁。面向對象設計原則概述軟件的可維護性和可復用性

軟件的復用(Reuse)或重用擁有眾多優(yōu)點,如可以提高軟件的開發(fā)效率,提高軟件質量,節(jié)約開發(fā)成本,恰當的復用還可以改善系統(tǒng)的可維護性。面向對象設計復用的目標在于實現支持可維護性的復用。

在面向對象的設計里面,可維護性復用都是以面向對象設計原則為基礎的,這些設計原則首先都是復用的原則,遵循這些設計原則可以有效地提高系統(tǒng)的復用性,同時提高系統(tǒng)的可維護性。第三十五頁,共九十七頁。面向對象設計原則概述軟件的可維護性和可復用性面向對象設計原則和設計模式也是對系統(tǒng)進行合理重構的指南針,重構(Refactoring)是在不改變軟件現有功能的基礎上,通過調整程序代碼改善軟件的質量、性能,使其程序的設計模式和架構更趨合理,提高軟件的擴展性和維護性。MartinFowler第三十六頁,共九十七頁。面向對象設計原則概述面向對象設計原則簡介常用的面向對象設計原則包括七個,這些原則并不是孤立存在的,它們相互依賴,相互補充。設計原則名稱設計原則簡介重要性單一職責原則(SingleResponsibilityPrinciple,SRP)類的職責要單一,不能將太多的職責放在一個類中?!铩铩铩铩铋_閉原則(Open-ClosedPrinciple,OCP)軟件實體對擴展是開放的,但對修改是關閉的,即在不修改一個軟件實體的基礎上去擴展其功能?!铩铩铩铩锢锸洗鷵Q原則(LiskovSubstitutionPrinciple,LSP)在軟件系統(tǒng)中,一個可以接受基類對象的地方必然可以接受一個子類對象?!铩铩铩铩钜蕾嚨罐D原則(DependencyInversionPrinciple,DIP)要針對抽象層編程,而不要針對具體類編程?!铩铩铩铩锝涌诟綦x原則(InterfaceSegregationPrinciple,ISP)使用多個專門的接口來取代一個統(tǒng)一的接口?!铩铩睢睢詈铣蓮陀迷瓌t(CompositeReusePrinciple,CRP)在系統(tǒng)中應該盡量多使用組合和聚合關聯關系,盡量少使用甚至不使用繼承關系?!铩铩铩铩畹厦滋胤▌t(LawofDemeter,LoD)一個軟件實體對其他實體的引用越少越好,或者說如果兩個類不必彼此直接通信,那么這兩個類就不應當發(fā)生直接的相互作用,而是通過引入一個第三者發(fā)生間接交互?!铩铩铩睢畹谌唔?,共九十七頁。單一職責原則單一職責原則定義單一職責原則(SingleResponsibilityPrinciple,SRP)定義如下:在軟件系統(tǒng)中,一個類只負責一個功能領域中的相應職責。另一種定義方式如下:就一個類而言,應該僅有一個引起它變化的原因。

第三十八頁,共九十七頁。單一職責原則單一職責原則分析一個類(或者大到模塊,小到方法)承擔的職責越多,它被復用的可能性越小。而且如果一個類承擔的職責過多,就相當于將這些職責耦合在一起,當其中一個職責變化時,可能會影響其他職責的運作。類的職責主要包括兩個方面:數據職責和行為職責,數據職責通過其屬性來體現,而行為職責通過其方法來體現。單一職責原則是實現高內聚、低耦合的指導方針,在很多代碼重構手法中都能找到它的存在,它是最簡單但又最難運用的原則,需要設計人員發(fā)現類的不同職責并將其分離,而發(fā)現類的多重職責需要設計人員具有較強的分析設計能力和相關重構經驗。第三十九頁,共九十七頁。單一職責原則單一職責原則實例實例說明某基于Java的C/S系統(tǒng)的“登錄功能”通過如下登錄類(Login)實現:現使用單一職責原則對其進行重構。第四十頁,共九十七頁。單一職責原則單一職責原則實例實例解析使用單一職責原則重構后的類圖:第四十一頁,共九十七頁。開閉原則開閉原則定義開閉原則(Open-ClosedPrinciple,OCP)定義如下:一個軟件實體應當對擴展開放,對修改關閉。也就是說在設計一個模塊的時候,應當使這個模塊可以在不被修改的前提下被擴展,即實現在不修改源代碼的情況下改變這個模塊的行為。第四十二頁,共九十七頁。開閉原則開閉原則分析開閉原則由BertrandMeyer于1988年提出,它是面向對象設計中最重要的原則之一。在開閉原則的定義中,軟件實體可以指一個軟件模塊、一個由多個類組成的局部結構或一個獨立的類。第四十三頁,共九十七頁。開閉原則開閉原則分析抽象化是開閉原則的關鍵。絕大部分的設計模式都符合開閉原則,在對每一個模式進行優(yōu)缺點評價時都會以開閉原則作為一個重要的評價依據,以判斷基于該模式設計的系統(tǒng)是否具備良好的靈活性和可擴展性。第四十四頁,共九十七頁。開閉原則開閉原則實例

實例說明某圖形界面系統(tǒng)提供了各種不同形狀的按鈕,客戶端代碼可針對這些按鈕進行編程,用戶可能會改變需求要求使用不同的按鈕,原始設計方案如圖所示:現對該系統(tǒng)進行重構,使之滿足開閉原則的要求。第四十五頁,共九十七頁。開閉原則開閉原則實例實例解析

第四十六頁,共九十七頁。里氏代換原則里氏代換原則定義里氏代換原則(LiskovSubstitutionPrinciple,LSP)有兩種定義方式,第一種定義方式相對嚴格,其定義如下:如果對每一個類型為S的對象o1,都有類型為T的對象o2,使得以T定義的所有程序P在所有的對象o1都代換成o2時,程序P的行為沒有變化,那么類型S是類型T的子類型。第二種更容易理解的定義方式如下:所有引用基類(父類)的地方必須能透明地使用其子類的對象。第四十七頁,共九十七頁。里氏代換原則里氏代換原則分析里氏代換原則由2008年圖靈獎得主、美國第一位計算機科學女博士、麻省理工學院BarbaraLiskov教授和卡內基-梅隆大學JeannetteWing教授于1994年提出。芭芭拉·利斯科夫(BarbaraLiskov),美國計算機科學家,2008年圖靈獎(計算機領域的諾貝爾獎)得主,2004年約翰·馮諾依曼獎得主。美國工程院院士,美國藝術與科學院院士,美國計算機協會會士?,F任麻省理工學院電子電氣與計算機科學系教授。她是美國第一個計算機科學女博士,第二位獲得圖靈獎的女科學家。第四十八頁,共九十七頁。里氏代換原則里氏代換原則分析里氏代換原則可以通俗表述為:在軟件中如果能夠使用基類對象,那么一定能夠使用其子類對象。把基類都替換成它的子類,程序將不會產生任何錯誤和異常,反過來則不成立,如果一個軟件實體使用的是一個子類的話,那么它不一定能夠使用基類。里氏代換原則是實現開閉原則的重要方式之一,由于使用基類對象的地方都可以使用子類對象,因此在程序中盡量使用基類類型來對對象進行定義,而在運行時再確定其子類類型,用子類對象來替換父類對象。第四十九頁,共九十七頁。里氏代換原則里氏代換原則分析喜歡動物喜歡貓因為貓是動物第五十頁,共九十七頁。里氏代換原則里氏代換原則實例實例說明某系統(tǒng)需要實現對重要數據(如用戶密碼)的加密處理,在數據操作類(DataOperator)中需要調用加密類中定義的加密算法,系統(tǒng)提供了兩個不同的加密類,CipherA和CipherB,它們實現不同的加密方法,在DataOperator中可以選擇其中的一個實現加密操作。如圖所示:第五十一頁,共九十七頁。里氏代換原則里氏代換原則實例實例說明如果需要更換一個加密算法類或者增加并使用一個新的加密算法類,如將CipherA改為CipherB,則需要修改客戶類Client和數據操作類DataOperator的源代碼,違背了開閉原則?,F使用里氏代換原則對其進行重構,使得系統(tǒng)可以靈活擴展,符合開閉原則。第五十二頁,共九十七頁。里氏代換原則里氏代換原則實例實例解析第五十三頁,共九十七頁。依賴倒轉原則依賴倒轉原則定義依賴倒轉原則(DependenceInversionPrinciple,DIP)的定義如下:高層模塊不應該依賴低層模塊,它們都應該依賴抽象。抽象不應該依賴于細節(jié),細節(jié)應該依賴于抽象。另一種表述為:要針對接口編程,不要針對實現編程。第五十四頁,共九十七頁。依賴倒轉原則依賴倒轉原則分析依賴倒轉原則是RobertC.Martin在1996年為《C++Reporter》所寫的專欄EngineeringNotebook的第三篇,后來加入到他在2002年出版的經典著作《AgileSoftwareDevelopment,Principles,Patterns,andPractices》中。第五十五頁,共九十七頁。依賴倒轉原則依賴倒轉原則分析簡單來說,依賴倒轉原則就是指:代碼要依賴于抽象的類,而不要依賴于具體的類;要針對接口或抽象類編程,而不是針對具體類編程。實現開閉原則的關鍵是抽象化,并且從抽象化導出具體化實現,如果說開閉原則是面向對象設計的目標的話,那么依賴倒轉原則就是面向對象設計的主要手段。第五十六頁,共九十七頁。依賴倒轉原則依賴倒轉原則分析依賴倒轉原則的常用實現方式之一是在代碼中使用抽象類,而將具體類放在配置文件中?!皩⒊橄蠓胚M代碼,將細節(jié)放進元數據”《程序員修煉之道:從小工到專家》第五十七頁,共九十七頁。依賴倒轉原則依賴倒轉原則分析類之間的耦合零耦合關系具體耦合關系抽象耦合關系依賴倒轉原則要求客戶端依賴于抽象耦合,以抽象方式耦合是依賴倒轉原則的關鍵。第五十八頁,共九十七頁。依賴倒轉原則依賴倒轉原則分析依賴注入第五十九頁,共九十七頁。依賴倒轉原則依賴倒轉原則分析依賴注入

構造注入(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(); }}第六十三頁,共九十七頁。依賴倒轉原則依賴倒轉原則實例實例說明某系統(tǒng)提供一個數據轉換模塊,可以將來自不同數據源的數據轉換成多種格式,如可以轉換來自數據庫的數據(DatabaseSource)、也可以轉換來自文本文件的數據(TextSource),轉換后的格式可以是xml文件(XMLTransformer)、也可以是xls文件(XLSTransformer)等。第六十四頁,共九十七頁。依賴倒轉原則依賴倒轉原則實例實例說明由于需求的變化,該系統(tǒng)可能需要增加新的數據源或者新的文件格式,每增加一個新的類型的數據源或者新的類型的文件格式,客戶類MainClass都需要修改源代碼,以便使用新的類,違背了開閉原則?,F使用依賴倒轉原則對其進行重構。第六十五頁,共九十七頁。依賴倒轉原則依賴倒轉原則實例實例解析第六十六頁,共九十七頁。接口隔離原則接口隔離原則定義接口隔離原則(InterfaceSegregationPrinciple,ISP)的定義如下:客戶端不應該依賴那些它不需要的接口。注意,在該定義中的接口指的是所定義的方法。另一種定義方法如下:一旦一個接口太大,則需要將它分割成一些更細小的接口,使用該接口的客戶端僅需知道與之相關的方法即可。第六十七頁,共九十七頁。接口隔離原則接口隔離原則分析

接口隔離原則是指使用多個專門的接口,而不使用單一的總接口。每一個接口應該承擔一種相對獨立的角色,不多不少,不干不該干的事,該干的事都要干。(1)一個接口就只代表一個角色,每個角色都有它特定的一個接口,此時這個原則可以叫做“角色隔離原則”。(2)接口僅僅提供客戶端需要的行為,即所需的方法,客戶端不需要的行為則隱藏起來,應當為客戶端提供盡可能小的單獨的接口,而不要提供大的總接口。第六十八頁,共九十七頁。接口隔離原則接口隔離原則分析使用接口隔離原則拆分接口時,首先必須滿足單一職責原則,將一組相關的操作定義在一個接口中,且在滿足高內聚的前提下,接口中的方法越少越好??梢栽谶M行系統(tǒng)設計時采用定制服務的方式,即為不同的客戶端提供寬窄不同的接口,只提供用戶需要的行為,而隱藏用戶不需要的行為。第六十九頁,共九十七頁。接口隔離原則接口隔離原則實例實例說明下圖展示了一個擁有多個客戶類的系統(tǒng),在系統(tǒng)中定義了一個巨大的接口AbstractService來服務所有的客戶類??梢允褂媒涌诟綦x原則對其進行重構。第七十頁,共九十七頁。接口隔離原則接口隔離原則實例實例解析第七十一頁,共九十七頁。合成復用原則合成復用原則定義合成復用原則(CompositeReusePrinciple,CRP)又稱為組合/聚合復用原則(Composition/AggregateReusePrinciple,CARP),其定義如下:盡量使用對象組合,而不是繼承來達到復用的目的。第七十二頁,共九十七頁。合成復用原則合成復用原則分析合成復用原則就是指在一個新的對象里通過關聯關系(包括組合關系和聚合關系)來使用一些已有的對象,使之成為新對象的一部分;新對象通過委派調用已有對象的方法達到復用其已有功能的目的。簡言之:要盡量使用組合/聚合關系,少用繼承。第七十三頁,共九十七頁。合成復用原則合成復用原則分析在面向對象設計中,可以通過兩種基本方法在不同的環(huán)境中復用已有的設計和實現,即通過組合/聚合關系或通過繼承。繼承復用:實現簡單,易于擴展。破壞系統(tǒng)的封裝性,從基類繼承而來的實現是靜態(tài)的,不可能在運行時發(fā)生改變,沒有足夠的靈活性;只能在有限的環(huán)境中使用。(“白箱”復用)組合/聚合復用:耦合度相對較低,選擇性地調用成員對象的操作;可以在運行時動態(tài)進行。(“黑箱”復用)第七十四頁,共九十七頁。合成復用原則合成復用原則分析組合/聚合可以使系統(tǒng)更加靈活,類與類之間的耦合度降低,一個類的變化對其他類造成的影響相對較少,因此一般首選使用組合/聚合來實現復用;其次才考慮繼承,在使用繼承時,需要嚴格遵循里氏代換原則,有效使用繼承會有助于對問題的理解,降低復雜度,而濫用繼承反而會增加系統(tǒng)構建和維護的難度以及系統(tǒng)的復雜度,因此需要慎重使用繼承復用。第七十五頁,共九十七頁。合成復用原則合成復用原則實例實例說明某教學管理系統(tǒng)部分數據庫訪問類設計如圖所示:第七十六頁,共九十七頁。合成復用原則合成復用原則實例實例說明如果需要更換數據庫連接方式,如原來采用JDBC連接數據庫,現在采用數據庫連接池連接,則需要修改DBUtil類源代碼。如果StudentDAO采用JDBC連接,但是TeacherDAO采用連接池連接,則需要增加一個新的DBUtil類,并修改StudentDAO或TeacherDAO的源代碼,使之繼承新的數據庫連接類,這將違背開閉原則,系統(tǒng)擴展性較差?,F使用合成復用原則對其進行重構。第七十七頁,共九十七頁。合成復用原則合成復用原則實例實例解析

第七十八頁,共九十七頁。迪米特法則迪米特法則定義迪米特法則(LawofDemeter,LoD)又稱為最少知識原則(LeastKnowledgePrinciple,LKP),它有多種定義方法,其中幾種典型定義如下:(1)不要和“陌生人”說話。(2)只與你的直接朋友通信。(3)每一個軟件單位對其他的單位都只有最少的知識,而且局限于那些與本單位密切相關的軟件單位。第七十九頁,共九十七頁。迪米特法則迪米特法則分析迪米特法則來自于1987年秋美國東北大學(NortheasternUniversity)一個名為“Demeter”的研究項目。簡單來說,迪米特法則就是指一個軟件實體應當盡可能少的與其他實體發(fā)生相互作用。這樣,當一個模塊修改時,就會盡量少的影響其他的模塊,擴展會相對容易,這是對軟件實體之間通信的限制,它要求限制軟件實體之間通信的寬度和深度。第八十頁,共九十七頁。迪米特法則迪米特法則分析在迪米特法則中,對于一個對象,其朋友包括以下幾類:(1)當前對象本身(this);(2)以參數形式傳入到當前對象方法中的對象;(3)當前對象的成員對象;(4)如果當前對象的成員對象是一個集合,那么集合中的元素也都是朋友;(5)當前對象所創(chuàng)建的對象。任何一個對象,如果滿足上面的條件之一,就是當前對象的“朋友”,否則就是“陌生人”。第八十一頁,共九十七頁。迪米特法則迪米特法則分析迪米特法則可分為狹義法則和廣義法則。在狹義的迪米特法則中,如果兩個類之間不必彼此直接通信,那么這兩個類就不應當發(fā)生直接的相互作用,如果其中的一個類需要調用另一個類的某一個方法的話,可以通過第三者轉發(fā)這個調用。第八十二頁,共九十七頁。迪米特法則迪米特法則分析狹義的迪米特法則:可以降低類之間的耦合,但是會在系統(tǒng)中增加大量的小方法并散落在系統(tǒng)的各個角落,它可以使一個系統(tǒng)的局部設計簡化,因為每一個局部都不會和遠距離的對象有直接的關聯,但是也會造成系統(tǒng)的不同模塊之間的通信效率降低,使得系統(tǒng)的不同模塊之間不容易協調。廣義的迪米特法則:指對對象之間的信息流量、流向以及信息的影響的控制,主要是對信息隱藏的控制。信息的隱藏可以使各個子系統(tǒng)之間脫耦,從而允許它們獨立地被開發(fā)、優(yōu)化、使用和修改,同時可以促進軟件的復用,由于每一個模塊都不依賴于其他模塊而存在,因此每一個模塊都可以獨立地在其他的地方使用。一個系統(tǒng)的規(guī)模越大,信息的隱藏就越重要,而信息隱藏的重要性也就越明顯。第八十三頁,共九十七頁。迪米特法則迪米特法則分析迪米特法則的主要用途在于控制信息的過載:在類的劃分上,應當盡量創(chuàng)建松耦合的類,類之間的耦合度越低,就越有利于復用,一個處在松耦合中的類一旦被修改,不會對關聯的類造成太大波及;在類的結構設計上,每一個類都應當盡量降低其成員變量和成員函數的訪問權限;在類的設計上,只要有可能,一個類型應當設計成不變類;在對其他類的引用上,一個對象對其他對象的引用應當降到最低。第八十四頁,共九十七頁。迪米特法則迪米特法則實例實例說明某系統(tǒng)界面類(如Form1、Form2等類)與數據訪問類(如DAO1、DAO2等類)之間的調用關系較為復雜,如圖所示:第八十五頁,共九十七頁。迪米特法則迪米特法則實例實例解析第八十六頁,共九十七頁。小結對于面向對象的軟件系統(tǒng)設計來說,在支持可維護性的同時,需要提高系統(tǒng)的可復用性。軟件的復用可以提高軟件的開發(fā)效率,提高軟件質量,節(jié)約開發(fā)成本,恰當的復用還可以改善系統(tǒng)的可維護性。單一職責原則要求在軟件系統(tǒng)中,一個類只負責一個功能領域中的相應職責。開閉原則要求一個軟件實體應當對擴展開放,對修改關閉,即在不修改源代碼的基礎上擴展一個系統(tǒng)的行為。里氏代換原則可以通俗表述為在軟件中如果能夠使用基類對象,那么一定能夠使用其子類對象。第八十七頁,共九十七頁。小結依賴倒轉原則要求抽象不應該依賴于細節(jié),細節(jié)應該依賴于抽象;要針對接口編程,不要針對實現編程。接口隔離原則要求客戶端不應該依賴那些它不需要的接口,即將一些大的接口細化成一些小的接口供客戶端使用。合成復用原則要求復用時盡量使用對象組合,而不使用繼承。迪米特法則要求一個軟件實體應當盡可能少的與其他實體發(fā)生相互作用。第八十八頁,共九十七頁。一些基本的設計模式AbstractFactory:提供一個創(chuàng)建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。Adapter:將一個類的接口轉換成客戶希望的另外一個接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。Bridge:將抽象部分與它的實現部分分離,使它們都可以獨立地變化。Builder:將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創(chuàng)建不同的表示。ChainofResponsibility:為解除請求的發(fā)送者和接收者之間耦合,而使多個對象都有機會處理這個請求。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它。Command:將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日志,以及支持可取消的操作。第八十九頁,共九十七頁。Composite:將對象組合成樹形結構以表示“部分-整體”的層次結構。它使得客戶對單個對象和復合對象的使用具有一致性。Decorator:動態(tài)地給一個對象添加一些額外的職責。就擴展功能而言,它比生成子類方式更為靈活。Fac

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論