1. 程式人生 > >iOS重構之面向協議程式設計實踐

iOS重構之面向協議程式設計實踐

iOS重構之面向協議程式設計實踐

最近一段時間都在進行iOS客戶端的重構,參考了許多iOS重構方面的資料,在重構的過程中也遇到一些困難,同時總結了不少經驗,在這裡和大家分享一下。這將會是一個系列的文章,每一篇文章都會從一個具體的、普遍性的問題出發,然後分析和解決這個問題。

插一句,軟體開發本身是一件工程化的事情,雖然有一些理論上的指導和支援,但終歸還是要從實際專案中出發,找到適合專案本身的最優解。我在這裡提到的一些實踐不一定適合每一個專案,甚至在某些情況下不適用,所以歡迎大家多多討論交流,我們的終極目標都是寫出具有可讀性、可維護性、可拓展性的高質量程式碼。

問題:如何管理介面跳轉的程式碼 回想一下你的專案中,大部分介面跳轉的程式碼是寫在哪裡的?最簡單方便的,直接寫在ViewController中。這種方式很好理解,因為ViewController原生就提供了介面跳轉的方法prsentVC/pushToVC。 隨著專案逐漸複雜,你可能會發現,有一些VC可以從很多不同的VC跳轉而來,如果按照原來的方式,就會有很多介面跳轉程式碼的重複。於是我們可能會建立一個XXRouter的類,把跳轉到XXViewController的程式碼提取出來,放到XXRouter中管理。

class XXRouter {
  var newVC:XXViewController {
    // 或者從Storyboard/Xib建立
    return XXViewController()
  }
  func presentXXVC(_ fromVC:UIViewController){
    let xxVC = newVC
    //XXViewController初始化
    vc.present(xxVC)
  }
}
複製程式碼

於是,在其他需要跳轉到XXViewController的VC中,只需要新建一個XXRouter,呼叫router.presentXXVC()就可以實現介面跳轉,也做到了統一管理。

class MyViewController:UIViewController {
  fileprivate var router = XXRouter()
  func someFunc() {
    // do something 
    router.presentXXVC()
  }
}
複製程式碼

這種方式已經能滿足我們大部分的要求了,只不過每次都需要建立一個Router例項,當然也可以使用單例。 其實我們可以藉助Swift的協議擴充套件Protocol Extension,可以把Router的實現變得更加直觀和優雅

協議擴充套件Router模式 Swift相比其他OOP語言的一個比較大的特點就是面向協議Protocol Oriented,理論知識這裡就不在贅述了,給一個蘋果的官方文件的連結Protocol-Oriented Programming in Swift 下面就是我們的實踐,這次我們使用protocol來處理我們的介面跳轉的程式碼

protocol XXRouter {}
extension XXRouter {
  var newVC:XXViewController {
    // 或者從Storyboard/Xib建立
    return XXViewController()
  }
  func presentXXVC(_ fromVC:UIViewController){
    let xxVC = newVC
    //XXViewController初始化
    vc.present(xxVC)
  }
}
複製程式碼

看起來和之前的實現幾乎一模一樣,但是在VC中我們就不需要建立XXRouter的例項,而是直接讓ViewController實現我們的XXRouter

extension MyViewController:XXRouter{}
class MyViewController:UIViewController {
  func someFunc() {
    // do something 
    self.presentXXVC(self)
  }
}
複製程式碼

這種方式還有一個好處就是可以很直觀的看到介面間的跳轉關係,尤其是一個介面可以跳轉到不同介面的情況,比如

extension MyViewController:XXRouter,YYRouter,ZZRouter{}
class MyViewController:UIViewController{}
複製程式碼

上面的程式碼幾乎達到了程式碼即文件:MyViewController這個介面,可以跳轉到XX、YY和ZZ三個ViewController。 更進一步,藉助extension的where條件擴充套件,我們還可以省略掉fromVC呢!


protocol XXRouter {}
extension XXRouter where Self:UIViewController {
  var newVC:XXViewController {
    // 或者從Storyboard/Xib建立
    return XXViewController()
  }
  
  func presentXXVC(){
    let xxVC = newVC
    // XXViewController初始化
    // 直接使用self,因為where指定當前擴充套件的是UIViewController
    self.present(xxVC)
  }
}
複製程式碼

這種方式修改的成本很小,只是提取和挪動介面間跳轉程式碼,幾乎不會出現什麼錯誤,用非常小的成本很大程度提高了專案程式碼的可讀性和可維護性。目前我們專案中的介面跳轉就是用這種方式進行了重構。

其他的跳轉方案 最後,再簡單分析一下其他兩種介面跳轉的解決方案

Storyboardsegue跳轉 這種方式對於Storyboard內ViewController之間的簡單跳轉來說十分方便,不用寫一行程式碼。而且.storyboard檔案中把一個個分離的ViewController通過圖的方式有機地連線在一起,清晰得展現出了專案內ViewController跳轉的路徑。

不足:涉及到頁面之間傳值或者需要做額外初始化工作的ViewController,需要在程式碼中實現delegate,和直接程式碼跳轉相比沒有太大差別。

對於需要額外初始化的ViewController,把一個跳轉流程分離到兩個地方實現,個人覺得更加不利於維護。 模仿前端的URL Router跳轉,比如MGJRouter 這種方式受Web URL跳轉方式的啟發,通過註冊頁面為URL Scheme的方式進行跳轉,比較適合Hybird應用,給跳轉網頁和跳轉ViewController一個統一的入口,便於維護

不足:額外多了註冊ViewController或實現Router Protocol的操作。 侵入性大,需要在專案初期就使用這種方式,不利於重構。 這篇文章就寫到這裡啦,iOS重構的文章還有挖了幾個坑,近期會填完!如果各位覺得本文對你有幫助的話,請點一個喜歡,謝謝~