軟件配置管理課件:第2章 常用重構技巧_第1頁
軟件配置管理課件:第2章 常用重構技巧_第2頁
軟件配置管理課件:第2章 常用重構技巧_第3頁
軟件配置管理課件:第2章 常用重構技巧_第4頁
軟件配置管理課件:第2章 常用重構技巧_第5頁
已閱讀5頁,還剩85頁未讀, 繼續免費閱讀

下載本文檔

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

文檔簡介

第2章常用重構技巧內容重構列表重新組織函數在對象之間搬移特性重新組織數據簡化條件表達式簡化函數調用處理概括關系大型重構重構列表重新組織函數(ComposingMethods)-9種方式ExtractMethodInlineMethodInlineTempReplaceTempwithQueryIntroduceExplainingVariableSplitTemporaryVariableRemoveAssignmentstoParametersReplaceMethodwithMethodObjectSubstituteAlgorithm重構列表在對象之間搬移特性(MovingFeaturesBetweenObjects)-8種方式MoveMethodMoveFieldExtractClassInlineClassHideDelegateRemoveMiddleManIntroduceForeignMethodIntroduceLocalExtension重構列表重新組織數據(OrganizingData)-16種方式SelfEncapsulateFieldReplaceDataValuewithObjectChangeValuetoReferenceChangeReferencetoValueReplaceArraywithObjectDuplicateObservedDataChangeUnidirectionalAssociationtoBidirectionalChangeBidirectionalAssociationtoUnidirectionalReplaceMagicNumberwithSymbolicConstantEncapsulateFieldEncapsulateCollectionReplaceRecordwithDataClassReplaceTypeCodewithClassReplaceTypeCodewithSubclassesReplaceTypeCodewithState/StrategyReplaceSubclasswithFields重構列表簡化條件表達式(SimplifyingConditionalExpressions)-8種方式DecomposeConditionalConsolidateConditionalExpressionConsolidateDuplicateConditionalFragmentsRemoveControlFlagReplaceNestedConditionalwithGuardClausesReplaceConditionalwithPolymorphismIntroduceNullObjectIntroduceAssertion重構列表簡化函數調用(MakingMethodCallsSimpler)-15種方式RenameMethodAddParameterRemoveParameterSeparateQueryfromModifierParameterizeMethodReplaceParameterwithExplicitMethodsPreserveWholeObjectReplaceParameterwithMethodIntroduceParameterObjectRemoveSettingMethodHideMethodReplaceConstructorwithFactoryMethodEncapsulateDowncastReplaceErrorCodewithExceptionReplaceExceptionwithTest重構列表處理概括關系(DealingwithGeneralization)-12種方式PullUpFieldPullUpMethodPullUpConstructorBodyPushDownMethodPushDownFieldExtractSubclassExtractSuperclassExtractInterfaceCollapseHierarchyFormTemplateMethodReplaceInheritancewithDelegationReplaceDelegationwithInheritance重構列表大型重構(BigRefactorings)-4種方式TeaseApartInheritanceConvertProceduralDesigntoObjectsSeparateDomainfromPresentationExtractHierarchy重新組織函數重新組織函數重新組織函數提煉函數(ExtractMethod)你有一段代碼可以被組織在一起并獨立出來。將這段代碼放進一個獨立函數中,并讓函數名稱解釋該函數的用途。voidprintOwing(doubleamount){

printBanner();//printdetails

System.out.println("name:"+_name);

System.out.println("amount"+amount);}voidprintOwing(doubleamount){

printBanner();

printDetails(amount);}voidprintDetails(doubleamount){

System.out.println("name:"+_name);

System.out.println("amount"+amount);}重新組織函數內聯函數(InlineMethod)一個函數的本體與名稱同樣清楚易懂。在函數調用點插入函數本體,然后移除該函數。

int

getRating(){return(moreThanFiveLateDeliveries())?2:1;}

boolean

moreThanFiveLateDeliveries(){return_numberOfLateDeliveries>5;}int

getRating(){return(_numberOfLateDeliveries>5)?2:1;}重新組織函數內聯臨時變量(InlineTemp)你有一個臨時變量,只被一個簡單表達式賦值一次,而它妨礙了其他重構手法。將所有對該變量的引用動作,替換為對它賦值的那個表達式自身。doublebasePrice=anOrder.basePrice();return(basePrice>1000)return(anOrder.basePrice()>1000)重新組織函數以查詢取代臨時變量(ReplaceTempwithQuery)你的程序以一個臨時變量(temp)保存某一表達式的運算結果。將這個表達式提煉到一個獨立函數(即查詢式Query)中。將這個臨時變量的所有引用點替換為對新函數的調用。此后,新函數就可被其他函數所使用。doublebasePrice=_quantity*_itemPrice;if(basePrice>1000)returnbasePrice*0.95;elsereturnbasePrice*0.98;if(basePrice()>1000)returnbasePrice()*0.95;elsereturnbasePrice()*0.98;...doublebasePrice(){return_quantity*_itemPrice;}重新組織函數引入解釋性變量(IntroduceExplainingVariable)你有一個復雜的表達式。將該復雜表達式(或其中一部分)的結果放進一個臨時變量,以此變量名稱來解釋表達式用途。if((platform.toUpperCase().indexOf("MAC")>-1)&&(browser.toUpperCase().indexOf("IE")>-1)&&

wasInitialized()&&resize>0){//dosomething}finalboolean

isMacOs=platform.toUpperCase().indexOf("MAC")>-1;finalboolean

isIEBrowser=browser.toUpperCase().indexOf("IE")>-1;finalboolean

wasResized=resize>0;if(isMacOs&&isIEBrowser&&wasInitialized()&&wasResized){//dosomething}重新組織函數分解臨時變量(SplitTemporaryVariable)你的程序有某個臨時變量被賦值超過一次,它既不是循環變量,也不是用來收集計算結果的變量。針對每次賦值,創造一個獨立的、對應的臨時變量。doubletemp=2*(_height+_width);

System.out.println(temp);temp=_height*_width;

System.out.println(temp);finaldoubleperimeter=2*(_height+_width);

System.out.println(perimeter);finaldoublearea=_height*_width;

System.out.println(area);重新組織函數移除對參數的賦值(RemoveAssignmentstoParameters)你的代碼對一個參數進行賦值動作。以一個臨時變量取代該參數的位置。

intdiscount(int

inputVal,intquantity,int

yearToDate){if(inputVal>50)inputVal-=2;

intdiscount(int

inputVal,intquantity,int

yearToDate){

intresult=inputVal;if(inputVal>50)result-=2;重新組織函數以函數對象取代函數(ReplaceMethodwithMethodObject)你有一個大型函數,其中對局部變量的使用使你無法釆用ExtractMethod。將這個函數放進一個單獨對象中,如此一來局部變量就成了對象內的字段。然后你可以在同一個對象中將這個大型函數分解為多個小型函數。classOrder...doubleprice(){doubleprimaryBasePrice;doublesecondaryBasePrice;doubletertiaryBasePrice;//longcomputation;...}重新組織函數替換算法(SubstituteAlgorithm)你想要把某個算法替換為另一個更清晰的算法。將函數本體(methodbody)替換為另一個算法。StringfoundPerson(String[]people){for(int

i=0;i<people.length;i++){if(people[i].equals("Don")){return"Don";}if(people[i].equals("John")){return"John";}if(people[i].equals("Kent")){return"Kent";}}return"";}StringfoundPerson(String[]people){Listcandidates=Arrays.asList(newString[]{"Don","John","Kent"});for(int

i=0;i<people.length;i++)if(candidates.contains(people[i]))returnpeople[i];return"";}在對象之間搬移特性在對象之間搬移特性在對象之間搬移特性搬移函數(MoveMethod)你的程序中,有個函數與其所駐類之外的另一個類進行更多交流:調用后者,或被后者調用?!臼褂昧硪粋€對象的次數比使用自己所駐對象的次數還多。】在該函數最常引用的類中建立一個有著類似行為的新函數。將舊函數變成一個單純的委托函數(delegatingmethod),或是將舊函數完全移除。在對象之間搬移特性搬移字段(MoveField)你的程序中,某個字段被其所駐類之外的另一個類更多地用到。在目標類中新建一個字段,修改源字段的所有用戶,令它們改用此新字段。在對象之間搬移特性提煉類(ExtractClass)某個類做了應該由兩(多)個類做的事。建立一個新類,將相關的字段和函數從舊類搬移到新類。在對象之間搬移特性將類內聯化(InlineClass)某個類沒有做太多事情(沒有承擔足夠的責任)。將這個類的所有特性搬移到另一個類中,然后移除原類。在對象之間搬移特性隱藏“委托關系”(HideDelegate)客戶通過一個委托類來調用另一個對象。在服務類上建立客戶所需的所有函數,用以隱藏委托關系。manager=john.getDepartment().getManager();manager=john.getManager();在對象之間搬移特性移除中間人(RemoveMiddleMan)某個類做了過多的簡單委托動作。讓客戶直接調用受托類。manager=john.getManager();manager=john.getDepartment().getManager();在對象之間搬移特性引入外加函數(IntroduceForeignMethod)你需要為提供服務的類增加一個函數,但你無法修改這個類。在客戶類中建立一個函數,并以第一參數形式傳入一個服務類實例。DatenewStart=newDate(previousEnd.getYear(),previousEnd.getMonth(),previousEnd.getDate()+1);DatenewStart=nextDay(previousEnd);privatestaticDatenextDay(Datearg){returnnewDate(arg.getYear(),arg.getMonth(),arg.getDate()+1);}在對象之間搬移特性引入本地擴展(IntroduceLocalExtension)你需要為服務類提供一些額外函數,但你無法修改這個類。建立一個新類,讓它包含這些額外函數。讓這個擴展品成為源類的子類或包裝類【適配器模式或裝飾模式】。重新組織數據重新組織數據重新組織數據自封裝字段(SelfEncapsulateField)你直接訪問一個字段,但與字段之間的耦合關系逐漸變得笨拙。為這個字段建立取值/設值(Getter/Setter)函數,并且只以這些函數來訪問字段。privateint_low,_high;booleanincludes(int

arg){returnarg>=_low&&arg<=_high;}privateint_low,_high;

booleanincludes(int

arg){returnarg>=getLow()&&arg<=getHigh();}

int

getLow(){return_low;}

int

getHigh(){return_high;}重新組織數據以對象取代數據值(ReplaceDataValuewithObject)你有一個數據項,需要與其他數據和行為一起使用才有意義。將數據項變成對象。重新組織數據將值對象改為引用對象(ChangeValuetoReference)你從一個類衍生出許多彼此相等的實例,希望將它們替換為同一個對象。將這個值對象變成一個引用對象。重新組織數據將引用對象改為值對象(ChangeReferencetoValue)你有一個引用對象,很小且不可變,而且不易管理。將它變成一個值對象。重新組織數據以對象取代數組(ReplaceArraywithObject)你有一個數組,其中的元素各自代表不同的東西。以對象替換數組。對于數組中的每個元素,以一個字段來表示。String[]row=newString[3];row[0]="Liverpool";row[1]="15";Performancerow=newPerformance();

row.setName("Liverpool");

row.setWins("15");重新組織數據復制“被監視數據”(DuplicateObservedData)你有一些領域數據置身于GUI控件中,而領域函數需要訪問這些數據。將這些數據復制到一個領域對象中。建立一個Observer模式,用以同步領域對象和GUI對象內的重復數據。重新組織數據將單向關聯改為雙向關聯(ChangeUnidirectionalAssociationtoBidirectional)兩個類都需要使用對方的特性,但其間只有一條單向連接。添加一個反向指針,并使修改雙方關系的函數能夠同時更新兩條連接。重新組織數據將雙向關聯改為單向關聯(ChangeBidirectionalAssociationtoUnidirectional)兩個類之間有雙向關聯,但其中一個類如今不再需要另一個類的特性。去除不必要的關聯。重新組織數據以字面常量取代魔法數(ReplaceMagicNumberwithSymbolicConstant)你有一個字面數值,帶有特別含義。創造一個常量,根據其意義為它命名,并將上述的字面數值替換為這個常量。doublepotentialEnergy(doublemass,doubleheight){returnmass*9.81*height;}

doublepotentialEnergy(doublemass,doubleheight){returnmass*GRAVITATIONAL_CONSTANT*height;}staticfinaldoubleGRAVITATIONAL_CONSTANT=9.81;重新組織數據封裝字段(EncapsulateField)你的類中存在一個public字段。將它聲明為private,并提供相應的訪問函數。publicString_name;privateString_name;publicStringgetName(){return_name;}publicvoidsetName(Stringarg){_name=arg;}重新組織數據封裝集合(EncapsulateCollection)有個函數返回一個集合。讓這個函數返回該集合的一個只讀副本,并在這個類中提供添加/移除集合元素的函數。重新組織數據以數據類取代記錄(ReplaceRecordwithDataClass)你需要面對傳統編程環境中的記錄結構(RecordStructure)。為該記錄創建一個“啞”數據對象。實例:將傳統的由JDBC返回的結果記錄,替換成一個簡單的值對象。重新組織數據以類取代類型碼(ReplaceTypeCodewithClass)類之中有一個數值類型碼,但它并不影響類的行為。以一個新的類替換該數值類型碼。重新組織數據以子類取代類型碼(ReplaceTypeCodewithSubclasses)你有一個不可變的類型碼,它會影響類的行為。以子類取代這個類型碼。重新組織數據以State/Strategy取代類型碼(ReplaceTypeCodewithState/Strategy)你有一個類型碼,它會影響類的行為,但你無法通過繼承手法消除它。以狀態對象或者具體策略對象取代類型碼。重新組織數據以字段取代子類(ReplaceSubclasswithFields)你的各個子類的唯一差別只在“返回常量數據”的函數身上。修改這些函數,使它們返回超類中的某個(新增)字段,然后銷毀子類。簡化條件表達式簡化條件表達式簡化條件表達式分解條件表達式(DecomposeConditional)你有一個復雜的條件(if-then-else)語句。從if、then、else三個段落中分別提煉出獨立函數。if(date.before(SUMMER_START)||date.after(SUMMER_END))charge=quantity*_winterRate+_winterServiceCharge;elsecharge=quantity*_summerRate;if(notSummer(date))charge=winterCharge(quantity);elsecharge=summerCharge(quantity);簡化條件表達式合并條件表達式(ConsolidateConditionalExpression)你有一系列條件測試,都得到相同結果。將這些測試合并為一個條件表達式,并將這個條件表達式提煉成為一個獨立函數。doubledisabilityAmount(){if(_seniority<2)return0;if(_monthsDisabled>12)return0;if(_isPartTime)return0;//computethedisabilityamountdoubledisabilityAmount(){if(isNotEligableForDisability())return0;//computethedisabilityamount簡化條件表達式合并重復的條件片段(ConsolidateDuplicateConditionalFragments)在條件表達式的每個分支上有著相同的一段代碼。將這段重復代碼搬移到條件表達式之外。if(isSpecialDeal()){total=price*0.95;send();}else{total=price*0.98;send();}if(isSpecialDeal())total=price*0.95;elsetotal=price*0.98;send();簡化條件表達式移除控制標記(RemoveControlFlag)在一系列布爾表達式中,某個變量帶有“控制標記”(controlflag)的作用。以break語句或return的語句取代控制標記。voidcheckSecurity(String[]people){

booleanfound=false;for(int

i=0;i<people.length;i++){if(!found){if(people[i].equals("Don")){

sendAlert();found=true;}if(people[i].equals("John")){

sendAlert();found=true;}}}}voidcheckSecurity(String[]people){for(int

i=0;i<people.length;i++){if(people[i].equals("Don")){

sendAlert();break;}if(people[i].equals("John")){

sendAlert();break;}}}簡化條件表達式以衛語句取代嵌套條件表達式(ReplaceNestedConditionalwithGuardClauses)函數中的條件邏輯(ConditionalLogic)使人難以看清正常的執行路徑。使用衛語句表現所有特殊情況。doublegetPayAmount(){doubleresult;if(_isDead)result=deadAmount();else{if(_isSeparated)result=separatedAmount();else{if(_isRetired)result=retiredAmount();elseresult=normalPayAmount();};}returnresult;}doublegetPayAmount(){if(_isDead)returndeadAmount();if(_isSeparated)returnseparatedAmount();if(_isRetired)returnretiredAmount();returnnormalPayAmount();}簡化條件表達式以多態取代條件表達式(ReplaceConditionalwithPolymorphism)你手上有個條件表達式,它可以根據對象類型的不同而選擇不同的行為。將這個條件表達式的每個分支放進一個子類內的覆寫函數中,然后將原始函數聲明為抽象函數。doublegetSpeed(){switch(_type){caseEUROPEAN:returngetBaseSpeed();caseAFRICAN:returngetBaseSpeed()-getLoadFactor()*_numberOfCoconuts;caseNORWEGIAN_BLUE:return(_isNailed)?0:getBaseSpeed(_voltage);}thrownewRuntimeException("Shouldbeunreachable");}簡化條件表達式引入Null對象(IntroduceNullObject)你需要再三檢查某對象是否為null。將null值替換為null對象。if(customer==null)plan=BillingPlan.basic();elseplan=customer.getPlan();簡化條件表達式引入斷言(IntroduceAssertion)某一段代碼需要對程序狀態做出某種假設。以斷言(assertion)明確表現這種假設。doublegetExpenseLimit(){//shouldhaveeitherexpenselimitoraprimaryprojectreturn(_expenseLimit!=NULL_EXPENSE)?_expenseLimit:_primaryProject.getMemberExpenseLimit();}doublegetExpenseLimit(){

Assert.isTrue(_expenseLimit!=NULL_EXPENSE||_primaryProject!=null);return(_expenseLimit!=NULL_EXPENSE)?_expenseLimit:_primaryProject.getMemberExpenseLimit();}簡化函數調用簡化函數調用簡化函數調用函數改名(RenameMethod)函數的名稱未能揭示函數的用途。修改函數名稱。簡化函數調用添加參數(AddParameter)某個函數需要從調用端得到更多信息。為此函數添加一個對象參數,讓該對象帶進函數所需信息。簡化函數調用移除參數(RemoveParameter)函數本體不再需要某個參數。將該參數去除。簡化函數調用將查詢函數和修改函數分離(SeparateQueryfromModifier)某個函數既返回對象狀態值,又修改對象狀態。建立兩個不同的函數,其中一個負責査詢,另一個負責修改。簡化函數調用令函數攜帶參數(ParameterizeMethod)若干函數做了類似的工作,但在函數本體中卻包含了不同的值。建立單一函數,以參數表達那些不同的值。簡化函數調用以明確函數取代參數(ReplaceParameterwithExplicitMethods)你有一個函數,其中完全取決于參數值而采取不同行為。針對該參數的每一個可能值,建立一個獨立函數。voidsetValue(Stringname,intvalue){if(name.equals("height"))_height=value;if(name.equals("width"))_width=value;

Assert.shouldNeverReachHere();}voidsetHeight(int

arg){_height=arg;}voidsetWidth(int

arg){_width=arg;}簡化函數調用保持對象完整(PreserveWholeObject)你從某個對象中取出若干值,將它們作為某一次函數調用時的參數。改為傳遞整個對象。

intlow=daysTempRange().getLow();

inthigh=daysTempRange().getHigh();

withinPlan=plan.withinRange(low,high);withinPlan=plan.withinRange(daysTempRange());簡化函數調用以函數取代參數(ReplaceParameterwithMethod)對象調用某個函數,并將所得結果作為參數,傳遞給另一個函數。而接受該參數的函數本身也能夠調用前一個函數。讓參數接受者去除該項參數,并直接調用前一個函數。

int

basePrice=_quantity*_itemPrice;

discountLevel=getDiscountLevel();doublefinalPrice=discountedPrice(basePrice,discountLevel);

int

basePrice=_quantity*_itemPrice;doublefinalPrice=discountedPrice(basePrice);簡化函數調用引入參數對象(IntroduceParameterObject)某些參數總是很自然地同時出現。以一個對象取代這些參數。簡化函數調用移除設值函數(RemoveSettingMethod)類中的某個字段應該在對象創建時被設值,然后就不再改變。去掉該字段的所有設值函數。簡化函數調用隱藏函數(HideMethod)有一個函數,從來沒有被其他任何類用到。將這個函數修改為private。簡化函數調用以工廠函數取代構造函數(ReplaceConstructorwithFactoryMethod)你希望在創建對象時不僅僅是做簡單的建構動作。將構造函數替換為工廠函數。Employee(inttype){_type=type;}staticEmployeecreate(inttype){returnnewEmployee(type);}簡化函數調用封裝向下轉型(EncapsulateDowncast)某個函數返回的對象,需要由函數調用者執行向下轉型(downcast)。將向下轉型動作移到函數中。ObjectlastReading(){returnreadings.lastElement();}ReadinglastReading(){return(Reading)readings.lastElement();}簡化函數調用以異常取代錯誤碼(ReplaceErrorCodewithException)某個函數返回一個特定的代碼,用以表示某種錯誤情況。改用異常。intwithdraw(intamount){if(amount>_balance)return-1;else{_balance-=amount;return0;}}voidwithdraw(intamount)throwsBalanceException{if(amount>_balance)thrownewBalanceException();_balance-=amount;}簡化函數調用以測試取代異常(ReplaceExceptionwithTest)面對一個調用者可預先檢查的條件,你拋出了一個異常。修改調用者,使它在調用函數之前先做檢查。doublegetValueForPeriod(int

periodNumber){try{return_values[periodNumber];}catch(ArrayIndexOutOfBoundsExceptione){return0;}}doublegetValueF

溫馨提示

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

評論

0/150

提交評論