從重構到吐血 - 我是如何刪掉 6 萬行代碼而且不刪減原有功能的

原文發表在 近期重構工做的一點收穫git

之前作我的項目的時候,簡歷上寫太重構了三次,後來在扇貝面試的時候,面試官問三次分別重構了什麼,仔細想一想那時候的重構並不算重構,第一次是 UI 改版,可是項目結構沒什麼大的變化,第二次是總體遷移到了 CocoaPods,此次勉強能算重構,第三次僅僅是變量名方法名空行這些地方的風格統一而已。面試

在如今工做的地方,接手這些項目以後,主要工做作的是重構,而重構工做,原本想寫成一行,結果發現挺多,我列個列表吧:json

  • 刪除沒用到的第三方庫
  • 刪除不合理的第三方庫,使用系統自帶的或者本身造輪子
  • 刪除定義好可是沒有用到的變量
  • 刪除 import 進來可是沒有用到的頭文件
  • 刪除更舊項目留下來的用不到的邏輯
  • Controller 層不合理的層級結構重構,無用代碼清理
  • View 層不合理的結構重構
  • Service 層冗餘的寫法重構
  • Model 層不合理的寫法重構
  • 拆開不合理的耦合
  • 耦合一個類別的模塊
  • 修復了多處內存泄露
  • 修復了多處循環引用
  • 優化編譯速度
  • 消除項目中的 warning

關於刪除代碼,在某個項目裏,Pods 文件夾那些第三方庫的代碼刪了 9 萬多行(那個目錄沒有被 git ignore 掉),項目裏面刪除了大約 4 萬行,其中大量代碼是該項目以前的項目裏面留下來的東西,只不過沒人清理。在刪了 4 萬行以後,程序仍然能完整的跑。後端

接下來是作了部分重構,把一些第三方庫刪掉,本身造輪子,在這個過程當中,累計刪除了 1.2 萬行代碼,增長了 1100 行左右。安全

整個重構工做下來,編譯速度從 2-3 分鐘減少到了 40 多秒,warning 從 70 多減小到了 0,第三方庫的數量從 51 個減小到了 13 個,安裝包從 22.1M 減少到了 3.7M,功能反而比以前還要多。網絡

內存泄露方面,由於沒人在乎這件事,有一個功能使用一次,就會增長好幾百 kb 內存,那部分代碼是用 C 寫的,因此及時釋放內存,而且優化下調用方式,內存泄露的問題就完美解決。app

循環引用方面,是由於有人把 Xcode 的 warning 關了,後來打開的時候,發現了四個循環引用 + 幾十個 warning,而且測試過程當中發現那個頁面不斷打開退出,程序會 crash。函數

籠統的就這麼多,我再來分享幾個具體的點。工具

避免濫用單例

單例用着確實爽,可是程序退出以前是不會被回收的,若是是整個生命週期基本用不到的模塊作成單例,那麼只會浪費內存而已。性能

避免無用的層級

具體是什麼意思呢,用網絡層舉例子,封裝 AFN 是一層,API 的後綴字符串放一層,構造請求放一層,OAuth 受權放一層,發普通請求又是一層。增長一個 API,至少要修改 6 個文件。寫着也很痛苦,看着也很痛苦啊。

網絡層就只設計一層,封裝 AFN,發請求的函數也在裏面,API 地址直接用字符串寫進去,搞那麼多層沒實際意義,在這麼小的一個項目裏面。

除此以外,關於項目文件結構,一兩個文件的建議不要新建文件夾放進去,這個主要是我的習慣,其實無大礙。

合理設計方法名

不留隱患

- (void)requestAtPathForRouteNamed:(NSString *)routeName object:(id)object parameters:(NSDictionary *)parameters
複製代碼

- (void)requestWithMethod:(XXHTTPMethod)method path:(NSString *)path params:(id)params paramsType:(XXParamType)paramsType
複製代碼

以前這樣設計的目的是 param 放 form data 類型數據,object 放 json 格式,顯然不合理,同一個 API 不該該容許同時存在 form data 和 json,若是採用第一種,新來的同事可能會認爲這兩個均可以填數據,這是不符合咱們指望的。

甚至再極端一點,某天咱們須要傳文件過去,是否是還得再擴充字段。

若是採用第二種,param 是 id 類型,若是是 json,type 傳入 json 枚舉類型,若是是二進制,type 傳入二進制枚舉類型,只留一個字段暴露給開發者更合理。

避免耦合

咱們的項目中有一條漸變顏色的線要處處用到,這條線咱們放在了 UIImage+XXUtil.h 裏面,以前的設計是這樣的:

+ (instancetype)xx_navigationBarShadowImage
複製代碼

在 .m 的實現中,還把 UIColor+XXTheme 耦合進去了,而且這個方法已經脫離了類名 Util 的實質,他已經不是一個通用的工具了,重構以後的命名是這樣的:

+ (instancetype)xx_gradientImageWithStartColor:(UIColor *)aColor endColor:(UIColor *)bColor andWidth:(CGFloat)width
複製代碼

這樣就很符合 Util 這個 category 名字。

避免濫用繼承

繼承確實很好用,帶來的後果就是子類會把父類的方法挨個執行一遍,乍一看沒什麼,可是若是這個方法很消耗性能呢。

咱們這個項目就遇到了,app 常常卡死,用着用着,就 freeze 了,點哪裏都沒反應。由於全部頁面都繼承自基類的一個設計,剛好基類裏面有一個比較耗時的操做,每一個頁面都會執行至少三次,就致使了頁面假死。

重構後的作法是設計成一個 category,只是給 UIViewController 添加了幾個方法,按需調用,不須要在每一個頁面都調用,因而解決了這個詭異的 bug。

合理選擇第三方庫

若是有一個功能,迫於各類緣由,不得不採用第三方庫,至少也要選一個 GitHub 上 star 比較多的吧,其次是看看 issue 列表有沒有什麼很嚴重的 bug 沒修好,以及兼容性問題,多養成好習慣,慢慢就能篩選出來最合適的庫了。

避免濫用第三方庫

咱們的項目以前有用到 YYText 這個庫,就爲了一段文字裏面加一張圖片,活動當天 iOS9 設備出現好幾百次 crash,實際上這段代碼用 NSAttributedString attributedStringWithAttachment 寫一下,七行就夠了,七行替代掉一個不穩定的第三方庫,仍是很划算的。

不知道由於什麼緣由,多是更舊的項目裏面用了 PSCollectionView,能跑就沒去重構,這類庫也是屬於徹底不必的,系統自帶的足夠好用,而且更安全。

各司其職

數據的處理,好比字符串進行 UTF8 編碼,時間戳轉成 YYYY-MM-DD 字符串,這些都放在 Model 層來處理,各司其職,Model 層就是作數據處理的。

結合實際需求

後端把各類 ID 用 long 型來記錄,是由於他們要作索引,爲了索引速度。而客戶端徹底沒這個需求,直接用 string 就好,還不用擔憂長度不夠的溢出,作展現的時候還不用轉類型。

一樣的,金額按理說應該用雙精度浮點型,由於 float 的精度不夠,結合個人開發經驗看,金額不多要客戶端作加減,直接用 string 便可,須要計算的時候再轉換,只轉換一次,避免丟失精度。

關於命名規範

蘋果的 UIKit 就是最好的例子,寫什麼組件不知道名字怎麼起的時候,就想一想蘋果有沒有相似的組件,去找找靈感。

避免無心義的註釋

OC 的方法名自己就很長很清晰了,只是給方法名中間加幾個空格,而後做爲註釋,跟沒寫同樣吧。

不用的代碼刪掉

Git 的做用就是隨時回溯之前版本,代碼都是能找到的,把代碼註釋掉,再寫一行相似的,除了增長閱讀成本,容易引發歧義,應該沒什麼用了。一個文件一共一兩百行,打開以後發現七八十行代碼被註釋了,這種感受至關操蛋,影響閱讀。

重構祖傳代碼真的會有一種活久見的感受,上面提到的那些是印象比較深入的,還有一些小問題已經悄悄解決了,但願我寫的代碼不會讓後面的同窗也這樣認爲吧。

相關文章
相關標籤/搜索