成為一個好的iOS架構師

架構沒有好壞之分,合適的架構就是好的架構。在選擇一個合適的架構方式前,要清楚需要做的事情、解決什么問題、業務方面需要得到什么,脫離業務談架構就是純粹的耍流氓。

架構原則:易讀性、易維護性、易擴展性。

本章會先介紹架構模式,在此基礎上討論組件化;然后討論下混合開發;最后梳理下設計模式。

架構模式

架構模式的出現時為了管理復雜的應用程序,這樣可以在一個時間內專門關注一個方面。例如,您可以在不依賴業務邏輯的情況下專注于視圖設計。同時也讓應用程序的測試更加容易。同時也簡化了分組開發。不同的開發人員可同時開發視圖、控制器邏輯和業務邏輯。我們經常說的MVC架構、MVVM架構屬于此類。

MVC

傳統移動APP開發的MVC架構:

1、短平快,實用于快速上線搶占市場
2、這種架構以層次結構簡單清晰,代碼容易開發而被大多數人所接受。

在MVC的體系架構中,Controller層負責整個APP中主要邏輯功能的實現;Model層則負責數據結構的描述以及數據持久化的功能;而View層作為展現層負責渲染整個APP的UI。分工清晰,簡潔明了;并且這種系統架構在語言框架層就得到了Apple的支持,所以非常適用于APP的startup開發。然后,這種架構在開發的后期會由于其超高耦和性,從而造就龐大Controller層,而這也是一直被人所詬病。最終的MVC都從Model-View-Controller走向了Massive-View-Controller 的終點。

當業務發展到一定層次,單一主營業務或單一App發展為百花齊放百家爭鳴的多元業務格局,MVC的弊端就開始無限放大,臃腫的Controller層會呈現出幾千行代碼,對于喜歡一個函數只做一件事的我而言,是完全無法接受的,也許有的人會選擇采用類別切分成多個Controller來假性“解決”。這時候,降低耦合,復用已有模塊成了架構的第一要務。
無論哪種架構,都能以MVC為基準,不斷調整重構、不斷劃分職責、不斷細化得來的。所以,能夠掌握如何劃分職責,將視圖、邏輯、數據三者連接起來,易用并方便維護,那么就可以了,無所謂什么模式。

MVP

作為MVC的進階版, 提出區分業務邏輯和業務展示, 將所有的業務邏輯轉移到P層, V層接受P層的數據更新通知進行頁面展示. 優點在于良好的分層帶來了友好的單元測試, 缺點在于分層會讓代碼邏輯優點繞, 同時也帶來了大量的代碼工作, 對程序員不夠友好.

MVVM

數據綁定做數據更新, 減少了大量的代碼工作, 同時優化了代碼邏輯, 只是學習成本有點高, 對新手不夠友好.

依照需求搭建你的iOS架構

對于絕大多數iOS開發者而言,日常的工作就是重復“網絡請求-》數據存儲-》頁面展示和交互”。再多一點就是埋點收集用戶數據,提供給產品或運營;自動化打包或自動化測試...

如何進行架構設計?

  • 搞清楚要解決那些問題,找到問題的充要條件
  • 問題分類,分模塊
  • 推演未來可能的走向,以備未來之需
  • 解決依賴關系中最基礎的問題,實現基礎模塊,然后再用基礎模塊堆疊出整個架構
  • 打點,跑單元測試,跑性能測試,對應優化

架構分層

常見的分層架構,有三層架構的:展現層、業務層、數據層。也有四層架構的:展現層、業務層、網絡層、本地數據層。

舉個例子:你要設計一個即時通訊的服務端架構,怎么分層?
記住,不要一上來就把三層架構的規范套上去,這樣做是做不出好架構的。你要先確定都需要解決哪些問題。這里只是舉例子,我隨意列出一點意思意思就好了:
要解決用戶登錄、退出的問題
解決不同用戶間數據交流的問題
解決用戶數據存儲的問題
如果是多臺服務器的集群,就要解決用戶連接的尋址問題

解決第一個問題需要一個鏈接管理模塊,鏈接管理模塊一般是通過鏈接池來實現。 解決第二個問題需要有一個數據交換模塊,從A接收來的數據要給到B,這個事情由這個模塊來做。 解決第三個問題需要有個數據庫,如果是服務于大量用戶,那么就需要一個緩沖區,只有當需要存儲的數據達到一定量時才執行寫操作。 解決第四個問題可以有幾種解決方案,一個是集群中有那么幾臺服務器作為尋路服務器,所有尋路的服務交給那幾臺去做,那么你需要開發一個尋路服務的Daemon。或者用廣播方式尋路,但如果尋路頻次非常高,會造成集群內部網絡負載特別大。這是你要權衡的地方,目前流行的思路是去中心化,那么要解決網絡負載的問題,你就可以考慮配置一個緩存。
于是我們有了這些模塊:
鏈接管理、數據交換、數據庫及其配套模塊、尋路模塊
做到這里還遠遠沒有結束,你要繼續針對這四個模塊繼續往下細分,直到足夠小為止。但是這里只是舉例子,所以就不往下深究了。

業務展示

業務層和展示層主要是要注意業務耦合度和基礎模塊的封裝。在此基礎上采用組件化。

網絡請求

網絡層在一個App中也是一個不可缺少的部分,工程師們在網絡層能夠發揮的空間也比較大。另外,蘋果對網絡請求部分已經做了很好的封裝,業界的AFNetworking也被廣泛使用。加入說需要你自己搭建一個HTTP框架,你需要考慮哪些模塊呢?
1、網絡層的安全機制;
思路:設計簽名,即在請求中攜帶同服務器商量好的密鑰hash出來的字符串。同時采用安全的HTTPS機制;
2、網絡層跟業務對接API的設計
思路:以什么方式傳送數據(delegate、notification、block、KVO、target-Action)、傳送什么樣的數據給業務層(一般APP從服務器得到的數據是Json格式,很多APP直接轉化成對象)
3、網絡層的優化方案
思路:連接復用、緩存

數據存儲

持久化方案不管是服務端還是客戶端,都是一個非常值得討論的話題。尤其是在服務端,持久化方案的優劣往往都會在一定程度上影響到產品的性能。然而在客戶端,只有為數不多的業務需求會涉及持久化方案,而且在大多數情況下,持久化方案對性能的要求并不是特別苛刻。所以我在移動端這邊做持久化方案設計的時候,考慮更多的是方案的可維護和可拓展,然后在此基礎上才是性能調優。

  • 根據需求決定持久化方案
  • 持久層與業務層之間的隔離
  • 持久層與業務層的交互方式
  • 數據遷移方案
  • 數據同步方案
思路:可維護性、可擴展性、性能消耗

在有需要持久化需求的時候,我們有非常多的方案可供選擇:NSUserDefault、KeyChain、File,以及基于數據庫的無數子方案。因此,當有需要持久化的需求的時候,我們首先考慮的是應該采用什么手段去進行持久化。

  • NSUserDefault
    一般來說,小規模數據,弱業務相關數據,都可以放到NSUserDefault里面,內容比較多的數據,強業務相關的數據就不太適合NSUserDefault了。我就見到過有些業務線會把大部分業務數據都塞到NSUserDefault里面去,當時看代碼的時候我特么就直接跪了。。。問起來為什么這么做?結果說因為寫起來方便~你妹。。。

  • keychain
    Keychain是蘋果提供的帶有可逆加密的存儲機制,普遍用在各種存密碼的需求上。另外,由于App卸載只要系統不重裝,Keychain中的數據依舊能夠得到保留,以及可被iCloud同步的特性,大家都會在這里存儲用戶唯一標識串。所以有需要加密、需要存iCloud的敏感小數據,一般都會放在Keychain。

  • 文件存儲
    文件存儲包括了Plist、archive、Stream等方式,一般結構化的數據或者需要方便查詢的數據,都會以Plist的方式去持久化。Archive方式適合存儲平時不太經常使用但很大量的數據,或者讀取之后希望直接對象化的數據,因為Archive會將對象及其對象關系序列化,以至于讀取數據的時候需要Decode很花時間,Decode的過程可以是解壓,也可以是對象化,這個可以根據具體<NSCoding>中的實現來決定。Stream就是一般的文件存儲了,一般用來存存圖片啊啥的,適用于比較經常使用,然而數據量又不算非常大的那種。
  • 數據庫存儲
    數據庫存儲的話,花樣就比較多了。蘋果自帶了一個Core Data,當然業界也有無數替代方案可選,不過真正用在iOS領域的除了Core Data外,就是FMDB比較多了。數據庫方案主要是為了便于增刪改查,當數據有狀態和類別的時候最好還是采用數據庫方案比較好,而且尤其是當這些狀態和類別都是強業務相關的時候,就更加要采用數據庫方案了。因為你不可能通過文件系統遍歷文件去甄別你需要獲取的屬于某個狀態或類別的數據,這么做成本就太大了。當然,特別大量的數據也不適合直接存儲數據庫,比如圖片或者文章這樣的數據,一般來說,都是數據庫存一個文件名,然后這個文件名指向的是某個圖片或者文章的文件。如果真的要做全文索引這種需求,建議最好還是掛個API丟到服務端去做。

功能組件化

功能組件化:將擁有獨立功能的代碼從系統中進行抽象并剝離,再以“插件”的形式插回原有系統中。剝離出功能組件,降低系統中模塊與模塊之間的耦和性,同時提高APP之間代碼的復用性。比如,廣告作為很多公司盈利的主要來源,抽取出一個廣告插件,使用的地方靈活配置絕對是所有人的做法。
當然,組件化一般情況下都會分為公有組件和業務組件。

- 公有組件

指的是封裝得比較好的一些SDK,包括一些第三方組件和自己內部使用的組件,比如人人稱道的AFNetworking、SDwebImage,都是這類組件的代表。

- 業務組件

則定義為包含了一系列業務功能的整體,例如登錄業務組件,注冊業務組件,賬戶業務組件即為此類組件的典型代表。對于業務組件,前期一般多會在使用的頁面生成class、傳遞參數、跳轉頁面。一種有效改良的方法是“采取了業務模塊注冊機制”來解除耦合,每個業務模塊對外提供相應的業務接口和短鏈接,短鏈接專門用一個類來映射對應的處理方法,靈活方便。而在業務組件,即業務模塊的內部,則可以根據不同開發人員的偏好,來實現不同的代碼架構。如上面討論的MVVM, MVP等,都可以在模塊內部進行而不影響整體系統架構。

- 組件化示例

項目的初期,不管用那種架構,我們經常會采用模塊之間互相調用,甚至模塊劃分不清晰,如圖:



造成嚴重耦合,為了解決上面的問題,可以考慮加一個中間層來協調模塊間的調用,所有的模塊間的調用都會經過中間層中轉。如下:



在組件進化過程中,很多大廠都提出了技術方案

Hybrid

移動APP的開發有兩種不同的路線,Native APP和Web APP。這兩種路線的區別類似于PC時代開發應用程序時的C/S架構和 B/S架構。

  • Native APP
    即所有的程序都由本地組件渲染完成。這類APP優點是顯而易見的,渲染速度快、用戶體驗好;缺點同時也十分突出:出現了錯誤一定要等待下一次用戶進行APP更新才能夠修復。

  • Web APP
    優點恰好就是Native APP的缺點所在,其頁面全部采用H5撰寫并存放在服務器端。每次進行頁面渲染時都從服務器請求最新的頁面。一旦頁面有錯誤服務器端進行更新便能立刻解決。不過其弊端也容易窺見:每次頁面都需要請求服務器,造成渲染時等待時間過長,從而導致的用戶體驗不夠完美,并且性能上較Native APP慢了1-2個數量級;與此同時還會導致更多的用戶流量消耗。另一個缺點則在于,Web APP在移動端上調用本地的硬件設備存在一定的不便。不過這些弊端也都有相應的解決方案,如PhoneGap將網頁提前打包在本地以減少網絡的請求時間;同時也提供一系列的插件來訪問本地的硬件設備。然而,盡管如此,其渲染速度上還是會稍微存在一定的差距。

  • Hybrid APP
    則是綜合了二者優缺點的解決方案。純粹展示性的模塊會更適合使用Web頁面來達到渲染的目的;而更多的數據操作性、動畫渲染性的模塊則更適合采用Native的方式。
    在Hot Patch被蘋果爸爸強制截斷,Hybrid會被推向一個新高度。是的,完全無法想象,出現bug,特別是重大問題后,你只能束手無策的提交新版本,耐心等待審核人員通過,告知用戶升級新版本。而典型的Hybrid,得到行業認可的是Facebook開源的React-Native,當然,目前國產大公司也有很多建樹,某寶的weex,我司已經開始大量投入使用(略微擔心蘋果親爸整出什么幺蛾子??)。
    也有很多H5直接和Native交互,以JSBridge的方式連接的方案進行動態部署,有名的CTJSBridge。

設計模式

設計模式可以通俗的理解為實現/解決某些問題,而形成的解決方案規范。增加代碼的可重用性,讓代碼能更容易理解和可靠。我們通常說所的代理模式、迭代器模式、策略模式就屬于這一類。對各種設計模式的了解可以幫助我們更快的解決編程過程中遇到的問題。
設計模式:設計模式主要分三個類型:創建型、結構型和行為型。傳送門-23種設計模式詳解
創建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。
其實還有兩類:并發型模式和線程池模式。

其中iOS主要重點用到的是以下幾種:

  • 單例模式
    當某個對象在整個程序中我們只需要一個,并且我們需要在不同的地方調用這個對象,獲取其中的屬性資源。這種時候我們就需要用到單例模式這種設計模式。
    示例:
    UIApplication類提供了 +sharedAPplication方法創建和獲取UIApplication單例
    NSBundle類提供了 +mainBunle方法獲取NSBundle單例
    NSFileManager類提供了 +defaultManager方法創建和獲得NSFileManager單例。(PS:有些時候我們得放棄使用單例模式,使用-init方法去實現一個新的實例,比如使用委托時)
    NSNotificationCenter提供了 +defaultCenter方法創建和獲取NSNotificationCenter單例(PS:該類還遵循了另一個重要的設計模式:觀察者模式)
    NSUserDefaults類提供了 +defaultUserDefaults方法去創建和獲取NSUserDefaults單例

** 一般我們習慣在定義方法share中使用dispatch_once函數(這個函數的作用就是保證block)來保證單例模式在整個程序中只被創建一次。但是之前看到有個同學是覆寫+(instancetype)allocWithZone:(struct _NSZone *)zone方法中添加代碼,保證只被創建一次。原因是別人可能并不知道你是單例,在生成的時候用[[class alloc]init]的形式,allocWithZone能保證不管哪種形式都能確保是單例。

  • 觀察者模式
    一個對象狀態改變,通知正在對他進行觀察的對象,這些對象根據各自要求做出相應的改變。操作對象向被觀察者對象投送消息,使得被觀察者的狀態得以改變,在此之前已經有觀察者向被觀察對象注冊,訂閱它的廣播,現在被觀察對象將自己狀態發生改變的消息廣播出來,觀察者接收到消息各自做出應變。
    示例:
    KVO(Key-Value-Observing)機制:
    通知(notification)機制,NSNotificationCenter

  • 代理模式
    就像是java中的接口,類可以實現或不實現協議(接口)中的方法。
    示例:
    UITableViewDelegate UITableViewDataSource

  • 策略模式

  • 簡單工廠

“簡單工廠模式是屬于創建型模式,又叫做靜態工廠方法(Static Factory Method)模式,但不屬于23種GOF設計模式之一。簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解為是不同工廠模式的一個特殊實現。”——百度百科
簡單工廠詳解及示例

  • 工廠模式

工廠模式是我們最常用的實例化對象模式了,是用工廠方法代替new操作的一種模式。著名的Jive論壇 ,就大量使用了工廠模式,工廠模式在Java程序系統可以說是隨處可見。因為工廠模式就相當于創建實例對象的new,我們經常要根據類Class生成實例對象,如A a=new A() 工廠模式也是用來創建實例對象的,所以以后new時就要多個心眼,是否可以考慮使用工廠模式,雖然這樣做,可能多做一些工作,但會給你系統帶來更大的可擴展性和盡量少的修改量。---百度百科
定義創建對象的接口,讓子類決定實例化哪一個類。工廠方法使得一個類的實例化延遲到子類。
工廠方式應該算是我們經常都會使用到的一種設計模式了,例如OC中的NSNumber,NSNumber可以提供int、BOOL、Float等相關類型的工廠方法來生產基于特定接口的不同類型對象。工廠模式的直觀意義可以理解為:通過固定工廠,生產不同形式的單一物品。例如我有一個輪胎工廠,我可以在工廠中生產適用于汽車、卡車、摩托車等適應的輪胎。
工廠模式詳解及示例

  • 抽象工廠模式

抽象工廠模式是所有形態的工廠模式中最為抽象和最具一般性的一種形態。抽象工廠模式是指當有多個抽象角色時,使用的一種工廠模式。抽象工廠模式可以向客戶端提供一個接口,使客戶端在不必指定產品的具體的情況下,創建多個產品族中的產品對象。根據里氏替換原則,任何接受父類型的地方,都應當能夠接受子類型。因此,實際上系統所需要的,僅僅是類型與這些抽象產品角色相同的一些實例,而不是這些抽象產品的實例。換言之,也就是這些抽象產品的具體子類的實例。工廠類負責創建抽象產品的具體子類的實例。---[百度百科]

  • 適配器模式

在計算機編程中,適配器模式(有時候也稱包裝樣式或者包裝)將一個類的接口適配成用戶所期待的。一個適配允許通常因為接口不兼容而不能在一起工作的類工作在一起,做法是將類自己的接口包裹在一個已存在的類中。---百度百科
簡而言之,就是引入一系列的操作取代之前對應的操作,同時又不影響以前的功能。
適配器模式模式詳解及示例

一篇關于“幾種設計模式對比”比較好的講解:傳送門

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容