一、什麼是重構
重構就是透過調整程式程式碼,但並不改變程式的功能特徵,達到改善軟體的質量、效能,使程式的設計模式和架構更趨合理,更容易被理解,提高軟體的擴充套件性和維護性。
需求的不斷變更是重構的最根本原因,而且重構是每一個開發人員都要面對的功課。
程式碼架構最初的設計也是經過精心的設計,具有良好架構的。但是隨著時間的推移、需求的劇增,必須不斷的修改原有的功能、追加新的功能,還免不了有一些缺陷需要修改。為了實現變更,不可避免的要違反最初的設計構架。經過一段時間以後,軟體的架構就千瘡百孔了。bug越來越多,越來越難維護,新的需求越來越難實現,最初的程式碼構架對新的需求漸漸的失去支援能力,而是成為一種制約。最後新需求的開發成本會超過開發一個新的軟體的成本,這就使這個app的生命走到了盡頭。
程式碼重構就能夠最大限度的避免這樣一種現象。系統發展到一定階段後,使用重構的方式,不改變系統的外部功能,只對內部的結構進行重新的整理。透過重構,不斷的調整系統的結構,使系統對於需求的變更始終具有較強的適應能力。
1、持續偏糾和改進軟體設計
2、使程式碼更被其他人所理解
3、幫助發現隱藏的程式碼缺陷
4、從長遠來看,有助於提高程式設計效率,增加專案進度(進度是質量的敵人,質量是進度的朋友)
1、模糊的不具有任何意義的方法名。
-(void)Ping:(NSString*)str;
重構為:
/**
* 初始化並啟動ping
*
* @param strProxyServer 要ping的伺服器
*/
-(void)autoPingProxyServer:(NSString*)strProxyServer
{
}
2、臃腫的類 。
3、長方法 。
4、大量的傳參。
-(void)studentRegister:(NSString *)school name:(NSString *)name class:(NSString *)class age:(int)age
-(void)studentRegister:(StudentModel *)model
5、模糊的方法名。
改為:
-(void)autoPingProxyServer:(NSString*)strProxyServer;
6. 現有的程式碼對它要實現的功能顯得過於複雜,並且你分析過它。
7. 修改後的程式碼遠比現存的程式碼邏輯要清晰。
8. 有足夠的時間,人手,財力來支援對專案進行迴歸測試。
9. 現有的程式碼陳舊無效率。
10. 無人認領的,寫的很爛的程式碼都屬於此類。
11. 程式碼禁區。這部分程式碼只有一個人可以修改,而且修改可能帶來更大的隱患。
1. 邏輯看起來過於複雜,沒時間去分析梳理。
2. 不理解為什麼前任程式設計師要這樣編寫。
3. 負責的是一個很重要的系統,而且時間很緊。
4. 團隊的新成員,或者開發者新接觸的語言
1、提取類/抽離方法
正如上面提到的,像“臃腫的類”(xmppmanager.h)這種程式碼臭味應該將原有類中的方法和屬性移動到適當數目的新類中去。舊類中對應新類的方法和屬性應該被移除。另外,有時候一些類過於臃腫是因為它包含了被其他類使用本應該是其他類的成員方法的成員方法。這些方法也應該被遷移到合適的類中。
3、分離條件
許多時候,一個方法很長是因為包含好幾個分支語句(if-else)。這些分支條件可以被提取和移動到幾個單獨的方法中。這確實能大大改善程式碼可讀性和可理解性。
If(date.before(monday) || date.after(friday) )
total=number*_rate+_fridaycharge;
else
total=number*_fridayrate;
if(notFriday(date))
total = mondaytotal(number);
elser
total = fridaytotal (number);
-(bool)notFriday(NSDate *)date
{
return date.before(monday) || date.after(friday);
}
-(void)mondaytotal(int)number
return number*_rate+_fridaycharge;
-(void)fridaytotal(int)number
return _fridaycharge;
4、引入引數物件/保留全域性物件
在我做程式碼審查時發現另外一個很常見的情況 - 好幾個引數被傳入方法。問題主要與需要從已有方法中增加或者移除一個方法引數有關。在這種場景,建議將相關方法引數組成一個物件(引入引數物件),讓方法傳遞這些物件而不是每個單獨的引數。
5、用符號常量替換無意義數字
對於有意義的並且到處被使用的字面常量,應該為它們分配一個命名常量。這能大大增強程式碼可讀性和可理解性。
If(iosSystemversion >7.)
#define iosSystemversion7 7 //ios系統為7
If(iosSystemversion > iosSystemversion)
6、重新命名方法
正如上面提到的,模糊不清的方法名會影響程式碼的可使用性。這些模糊不清的名稱應該重新命名為有意義的可能與業務術語有關的名稱,來幫助開發者透過業務上下文更好地理解程式碼。
-(void)function(nsstring *)str; //擷取string
-(void)trimstring(nsstring *)str; //擷取string
7、記憶體最佳化
檢查工程的記憶體使用情況,及時Release。
8、去除硬編碼,將文案統一至模組化的宏定義檔案
將一些透過的文案,常用的文案統一便寫到宏定義檔案
#define ASK_CREATEQUESTION @" "
#define ASK_HOSTURL @"/"
#define ASK_CREATEQUESTION ASK_HOSTURL@""
1、程式碼規範
a.圖片用英文名:例如:ToolView_Emotion_HL.png
錯誤: 確定.png, [email protected]
b.程式碼註釋規範。 例如: int age;//年齡
c.宏定義規範(涉及到全域性的,環境變化的)。
例如:#define PACHAT_ENVIRONMENT 1 //0:int 開發環境 1:stg 測試環境 2:prd 生產環境
d.類名,方法名,變數名命名規範,要有意思,最好用英語。
例如:
#pragma mark -
g.新建資料夾要在相應的工程目錄資料夾裡面新建
2、當屬性、方法或類存在任何的需要複用的意向時,歸納提煉它們。
3、不要低估小方法對程式碼整潔的作用。使用小方法能讓你節省很多筆墨。
4、用封裝控制可見度。
5、消除依賴。
6、簡化構造方法——即使這樣做會使程式碼變複雜。
7、不確定時,將計算操作移入到這些資料的所有者物件裡,或將資料移動到執行計算操作的物件裡。
8、使用小物件,松耦合,避免大物件,高聚合。
-(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
//TODO:大量的業務邏輯處理
- (void)dealwithphotoInfo:(NSDictionary *)info
//TODO: 具體的業務邏輯處理
….
NSMutableDictionary *dicinfo = [NSMutableDictionary dictionaryWithDictionary:info];
[dicinfo setObject:[NSNumber numberWithInt:picker.sourceType ] forKey:@"camreSourceType"];
[self performSelector:@selector(dealwithphotoInfo:) withObject:dicinfo afterDelay:0.1]; // 延時處理圖片壓縮和儲存
[picker dismissViewControllerAnimated:YES completion:nil];
9、使用代理物件,模擬物件和輔助物件來隔離網路,資料庫,檔案和使用者介面。
10、不確定時,儘量在model裡新增程式碼,必要時才往controler新增程式碼。view裡新增的都應該是便捷功能和簡寫方法,但不要侷限於此。
1. 使用自動化的迴歸測試,快速的驗證你的修改。這非常重要,如果沒有準備自動化測試,你應該在做任何修改前建好它。
2. 儘量讓你的重構處於很短的開發週期,產品更新發布週期也儘可能短。
3. 把你重構的程式碼和其它程式隔離開,這樣能讓你更容易找到出問題的地方。
4. 為你的重構活動準備測試計劃,包括迴歸測試,功能測試,反向測試,負載測試,效能測試和使用者確認測試。
5. 投入全部精力來研究其中的邏輯,不要分心做其它事情。
6. 在需要的地方使用設計模式。不要為了設計模式而增加設計模式。設計模式應該用在合適的時間和合適地方。
一、什麼是重構
重構就是透過調整程式程式碼,但並不改變程式的功能特徵,達到改善軟體的質量、效能,使程式的設計模式和架構更趨合理,更容易被理解,提高軟體的擴充套件性和維護性。
二、為什麼要程式碼重構需求的不斷變更是重構的最根本原因,而且重構是每一個開發人員都要面對的功課。
程式碼架構最初的設計也是經過精心的設計,具有良好架構的。但是隨著時間的推移、需求的劇增,必須不斷的修改原有的功能、追加新的功能,還免不了有一些缺陷需要修改。為了實現變更,不可避免的要違反最初的設計構架。經過一段時間以後,軟體的架構就千瘡百孔了。bug越來越多,越來越難維護,新的需求越來越難實現,最初的程式碼構架對新的需求漸漸的失去支援能力,而是成為一種制約。最後新需求的開發成本會超過開發一個新的軟體的成本,這就使這個app的生命走到了盡頭。
程式碼重構就能夠最大限度的避免這樣一種現象。系統發展到一定階段後,使用重構的方式,不改變系統的外部功能,只對內部的結構進行重新的整理。透過重構,不斷的調整系統的結構,使系統對於需求的變更始終具有較強的適應能力。
三、透過程式碼重構可以達到以下的目標1、持續偏糾和改進軟體設計
2、使程式碼更被其他人所理解
3、幫助發現隱藏的程式碼缺陷
4、從長遠來看,有助於提高程式設計效率,增加專案進度(進度是質量的敵人,質量是進度的朋友)
四、何時需要程式碼重構1、模糊的不具有任何意義的方法名。
-(void)Ping:(NSString*)str;
重構為:
/**
* 初始化並啟動ping
*
* @param strProxyServer 要ping的伺服器
*/
-(void)autoPingProxyServer:(NSString*)strProxyServer
{
}
2、臃腫的類 。
3、長方法 。
4、大量的傳參。
-(void)studentRegister:(NSString *)school name:(NSString *)name class:(NSString *)class age:(int)age
{
}
重構為:
-(void)studentRegister:(StudentModel *)model
{
}
5、模糊的方法名。
-(void)Ping:(NSString*)str;
改為:
-(void)autoPingProxyServer:(NSString*)strProxyServer;
6. 現有的程式碼對它要實現的功能顯得過於複雜,並且你分析過它。
7. 修改後的程式碼遠比現存的程式碼邏輯要清晰。
8. 有足夠的時間,人手,財力來支援對專案進行迴歸測試。
9. 現有的程式碼陳舊無效率。
10. 無人認領的,寫的很爛的程式碼都屬於此類。
11. 程式碼禁區。這部分程式碼只有一個人可以修改,而且修改可能帶來更大的隱患。
五、哪些情況不需要考慮程式碼重構1. 邏輯看起來過於複雜,沒時間去分析梳理。
2. 不理解為什麼前任程式設計師要這樣編寫。
3. 負責的是一個很重要的系統,而且時間很緊。
4. 團隊的新成員,或者開發者新接觸的語言
六、重構的具體方法1、提取類/抽離方法
正如上面提到的,像“臃腫的類”(xmppmanager.h)這種程式碼臭味應該將原有類中的方法和屬性移動到適當數目的新類中去。舊類中對應新類的方法和屬性應該被移除。另外,有時候一些類過於臃腫是因為它包含了被其他類使用本應該是其他類的成員方法的成員方法。這些方法也應該被遷移到合適的類中。
3、分離條件
許多時候,一個方法很長是因為包含好幾個分支語句(if-else)。這些分支條件可以被提取和移動到幾個單獨的方法中。這確實能大大改善程式碼可讀性和可理解性。
If(date.before(monday) || date.after(friday) )
{
total=number*_rate+_fridaycharge;
}
else
{
total=number*_fridayrate;
}
重構為:
if(notFriday(date))
{
total = mondaytotal(number);
}
elser
{
total = fridaytotal (number);
}
-(bool)notFriday(NSDate *)date
{
return date.before(monday) || date.after(friday);
}
-(void)mondaytotal(int)number
{
return number*_rate+_fridaycharge;
}
-(void)fridaytotal(int)number
{
return _fridaycharge;
}
4、引入引數物件/保留全域性物件
在我做程式碼審查時發現另外一個很常見的情況 - 好幾個引數被傳入方法。問題主要與需要從已有方法中增加或者移除一個方法引數有關。在這種場景,建議將相關方法引數組成一個物件(引入引數物件),讓方法傳遞這些物件而不是每個單獨的引數。
5、用符號常量替換無意義數字
對於有意義的並且到處被使用的字面常量,應該為它們分配一個命名常量。這能大大增強程式碼可讀性和可理解性。
If(iosSystemversion >7.)
重構為:
#define iosSystemversion7 7 //ios系統為7
If(iosSystemversion > iosSystemversion)
6、重新命名方法
正如上面提到的,模糊不清的方法名會影響程式碼的可使用性。這些模糊不清的名稱應該重新命名為有意義的可能與業務術語有關的名稱,來幫助開發者透過業務上下文更好地理解程式碼。
-(void)function(nsstring *)str; //擷取string
-(void)trimstring(nsstring *)str; //擷取string
7、記憶體最佳化
檢查工程的記憶體使用情況,及時Release。
8、去除硬編碼,將文案統一至模組化的宏定義檔案
將一些透過的文案,常用的文案統一便寫到宏定義檔案
#define ASK_CREATEQUESTION @" "
重構為:
#define ASK_HOSTURL @"/"
#define ASK_CREATEQUESTION ASK_HOSTURL@""
七、當重構沒有現成的明顯的方向時,我們可以遵循下面的原則1、程式碼規範
a.圖片用英文名:例如:ToolView_Emotion_HL.png
錯誤: 確定.png, [email protected]
b.程式碼註釋規範。 例如: int age;//年齡
c.宏定義規範(涉及到全域性的,環境變化的)。
例如:#define PACHAT_ENVIRONMENT 1 //0:int 開發環境 1:stg 測試環境 2:prd 生產環境
d.類名,方法名,變數名命名規範,要有意思,最好用英語。
例如:
#pragma mark -
g.新建資料夾要在相應的工程目錄資料夾裡面新建
2、當屬性、方法或類存在任何的需要複用的意向時,歸納提煉它們。
3、不要低估小方法對程式碼整潔的作用。使用小方法能讓你節省很多筆墨。
4、用封裝控制可見度。
5、消除依賴。
6、簡化構造方法——即使這樣做會使程式碼變複雜。
7、不確定時,將計算操作移入到這些資料的所有者物件裡,或將資料移動到執行計算操作的物件裡。
8、使用小物件,松耦合,避免大物件,高聚合。
-(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
//TODO:大量的業務邏輯處理
}
重構為:
- (void)dealwithphotoInfo:(NSDictionary *)info
{
//TODO: 具體的業務邏輯處理
….
}
-(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSMutableDictionary *dicinfo = [NSMutableDictionary dictionaryWithDictionary:info];
[dicinfo setObject:[NSNumber numberWithInt:picker.sourceType ] forKey:@"camreSourceType"];
[self performSelector:@selector(dealwithphotoInfo:) withObject:dicinfo afterDelay:0.1]; // 延時處理圖片壓縮和儲存
[picker dismissViewControllerAnimated:YES completion:nil];
}
9、使用代理物件,模擬物件和輔助物件來隔離網路,資料庫,檔案和使用者介面。
10、不確定時,儘量在model裡新增程式碼,必要時才往controler新增程式碼。view裡新增的都應該是便捷功能和簡寫方法,但不要侷限於此。
八、如何降低重構的風險1. 使用自動化的迴歸測試,快速的驗證你的修改。這非常重要,如果沒有準備自動化測試,你應該在做任何修改前建好它。
2. 儘量讓你的重構處於很短的開發週期,產品更新發布週期也儘可能短。
3. 把你重構的程式碼和其它程式隔離開,這樣能讓你更容易找到出問題的地方。
4. 為你的重構活動準備測試計劃,包括迴歸測試,功能測試,反向測試,負載測試,效能測試和使用者確認測試。
5. 投入全部精力來研究其中的邏輯,不要分心做其它事情。
6. 在需要的地方使用設計模式。不要為了設計模式而增加設計模式。設計模式應該用在合適的時間和合適地方。