iOS應用架構談(1):開篇

發表於2015-05-29

緣由

之前安居客iOS app的第二版架構大部分內容是我做的,期間有總結了一些經驗。在將近一年之後,前同事zzz在微信朋友圈上發了一個問題:假如問你一個iOS or Android app的架構,你會從哪些方面來說呢?

當時看到這個問題正好在乘公車回家的路上,閒來無聊就答了一把。在zzz在微信朋友圈上追問了幾個問題之後,我覺得有必要開個部落格專門來講講一些個人見解。

其實對於iOS客戶端應用的架構來說,複雜度不亞於服務端,但側重點和入手點卻跟服務端不太一樣。比如客戶端應用就不需要考慮類似C10K的問題,正常的app就根本不需要考慮。

這系列文章我會主要專注在iOS應用架構方面,很多方案也是基於iOS技術棧的特點而建立的。因為我個人不是很喜歡寫Java,所以Android這邊的我就不太瞭解了。如果你是Android開發者,你可以側重看我提出的一些架構思想,畢竟不管做什麼,思路是相通的,實現手段不同罷了。

當我們討論客戶端應用架構的時候,我們在討論什麼?

其實市面上大部分應用不外乎就是顛過來倒過去地做以下這些事情:

簡單來說就是調API,展示頁面,然後跳轉到別的地方再調API,再展示頁面。

那這特麼有毛好架構的?

非也,非也。 —- 包不同 《天龍八部》

App確實就是主要做這些事情,但是支撐這些事情的基礎,就是做架構要考慮的事情。

  • 呼叫網路API
  • 頁面展示
  • 資料的本地持久化
  • 動態部署方案

上面這四大點,稍微細說一下就是:

  • 如何讓業務開發工程師方便安全地呼叫網路API?然後儘可能保證使用者在各種網路環境下都能有良好的體驗?
  • 頁面如何組織,才能儘可能降低業務方程式碼的耦合度?儘可能降低業務方開發介面的複雜度,提高他們的效率?
  • 當資料有在本地存取的需求的時候,如何能夠保證資料在本地的合理安排?如何儘可能地減小效能消耗?
  • iOS應用有稽核週期,如何能夠通過不發版本的方式展示新的內容給使用者?如何修復緊急bug?

上面幾點是針對App說的,下面還有一些是針對團隊說的:

  • 收集使用者資料,給產品和運營提供參考
  • 合理地組織各業務方開發的業務模組,以及相關基礎模組
  • 每日app的自動打包,提供給QA工程師的測試工具

一時半會兒我還是隻能想到上面這三點,事實上應該還會有很多,想不起來了。

所以當我們討論客戶端應用架構的時候,我們討論的差不多就是這些問題。

這系列文章要回答那些問題?

這系列文章主要是回答以下這些問題:

  1. 網路層設計方案?設計網路層時要考慮哪些問題?對網路層做優化的時候,可以從哪些地方入手?
  2. 頁面的展示、呼叫和組織都有哪些設計方案?我們做這些方案的時候都要考慮哪些問題?
  3. 本地持久化層的設計方案都有哪些?優劣勢都是什麼?不同方案間要注意的問題分別都是什麼?
  4. 要實現動態部署,都有哪些方案?不同方案之間的優劣點,他們的側重點?

本文要回答那些問題?

上面細分出來的四個問題,我會分別在四篇文章裡面寫。那麼這篇文章就是來講一些通識啥的,也是開個坑給大家討論通識問題的。

架構設計的方法

所有事情最難的時候都是開始做的時候,當你開始著手設計並實現某一層的架構乃至整個app的架構的時候,很有可能會出現暫時的無從下手的情況。以下方法論是我這些年總結出來的經驗,每個架構師也一定都有一套自己的方法論,但一樣的是,不管你採用什麼方法,全域性觀、高度的程式碼審美能力、靈活使用各種設計模式一定都是貫穿其中的。歡迎各位在評論區討論。

第一步:搞清楚要解決哪些問題,並找到解決這些問題的充要條件

你必須得清楚你要做什麼,業務方希望要什麼。而不是為了架構而架構,也不是為了體驗新技術而改架構方案。以前是MVC,最近流行MVVM,如果過去的MVC是個好架構,沒什麼特別大的缺陷,就不要推倒然後搞成MVVM。

關於充要條件我也要說明一下,有的時候系統提供的函式是需要額外引數的,比如read函式。還有翻頁的時候,當前頁碼也是充要條件。但對於業務方來說,這些充要條件還能夠再縮減。

比如read,需要給出file descriptor,需要給出buf,需要給出size。但是對於業務方來說,充要條件就只要file descriptor就夠了。再比如翻頁,其實業務方並不需要記錄當前頁號,你給他暴露一個loadNextPage這樣的方法就夠了。

搞清楚對於業務方而言的真正充要條件很重要!這決定了你的架構是否足夠易用。另外,傳的引數越少,耦合度相對而言就越小,你替換模組或者升級模組所花的的代價就越小。

第二步:問題分類,分模組

這個不用多說了吧。

第三步:搞清楚各問題之間的依賴關係,建立好模組交流規範並設計模組

關鍵在於建立一套統一的交流規範。這一步很能夠體現架構師在軟體方面的價值觀,雖然存在一定程度上的好壞優劣(比如胖Model和瘦Model),但既然都是架構師了,基本上是不會設計出明顯很爛的方案的,除非這架構師還不夠格。所以這裡是架構師價值觀輸出的一個視窗,從這一點我們是能夠看出架構師的素質的。

另外要注意的是,一定是建立一套統一的交流規範,不是兩套,不是多套。你要堅持你的價值觀,不要搖擺不定。要是搞出各種五花八門的規範出來,一方面有不切實際的炫技嫌疑,另一方面也會帶來後續維護的災難。

第四步:推演預測一下未來可能的走向,必要時新增新的模組,記錄更多的基礎資料以備未來之需

很多稱職的架構師都會在這時候考慮架構未來的走向,以及考慮做完這一輪架構之後,接下來要做的事情。一個好的架構雖然是功在當代利在千秋的工程,但絕對不是一個一勞永逸的工程。軟體是有生命的,你做出來的架構決定了這個軟體它這一生是坎坷還是幸福。

第五步:先解決依賴關係中最基礎的問題,實現基礎模組,然後再用基礎模組堆疊出整個架構

這一步也是驗證你之前的設計是否合理的一步,隨著這一步的推進,你很有可能會遇到需要對架構進行調整的情況。這個階段一定要吹毛求疵高度負責地去開發,不要得過且過,發現架構有問題就及時調整。否則以後調整的成本就非常之大了。

第六步:打點,跑單元測試,跑效能測試,根據資料去優化對應的地方

你得用這些資料去向你的boss邀功,你也得用這些資料去不斷調整你的架構。

總而言之就是要遵循這些原則:自頂向下設計(1,2,3,4步),自底向上實現(5),先測量,後優化(6)。

什麼樣的架構師是好架構師?

  1. 每天都在學習,新技術新思想上手速度快,理解速度快

做不到這點,你就是碼農

  1. 業務出身,或者至少非常熟悉公司所處行業或者本公司的業務

做不到這點,你就是運維

  1. 熟悉軟體工程的各種規範,踩過無數坑。不會為了完成需求不擇手段,不推崇quick & dirty

做不到這點,你比較適合去競爭對手那兒當工程師

  1. 及時承認錯誤,不要覺得承認錯誤會有損你架構師的身份

做不到這點,公關行業比較適合你

  1. 不為了炫技而炫技

做不到這點,你就是高中程式設計愛好者

  1. 精益求精

做不到這點,(我想了好久,但我還是不知道你適合去幹什麼。)

什麼樣的架構叫好架構?

  1. 程式碼整齊,分類明確,沒有common,沒有core
  2. 不用文件,或很少文件,就能讓業務方上手
  3. 思路和方法要統一,儘量不要多元
  4. 沒有橫向依賴,萬不得已不出現跨層訪問
  5. 對業務方該限制的地方有限制,該靈活的地方要給業務方創造靈活實現的條件
  6. 易測試,易擴充
  7. 保持一定量的超前性
  8. 介面少,介面引數少
  9. 高效能

以上是我判斷一個架構是不是好架構的標準,這是根據重要性來排列的。客戶端架構跟服務端架構要考慮的問題和側重點是有一些區別的。下面我會針對每一點詳細講解一下:

程式碼整齊,分類明確,沒有common,沒有core

程式碼整齊是每一個工程師的基本素質,先不說你搞定這個問題的方案有多好,解決速度有多快,如果程式碼不整齊,一切都白搭。因為你的程式碼是要給別人看的,你自己也要看。如果哪一天架構有修改,正好改到這個地方,你很容易自己都看不懂。另外,破窗理論提醒我們,如果程式碼不整齊分類不明確,整個架構會隨著一次一次的擴充而越來越混亂。

分類明確的字面意思大家一定都瞭解,但還有一個另外的意思,那就是:不要讓一個類或者一個模組做兩種不同的事情。如果有類或某模組做了兩種不同的事情,一方面不適合未來擴充,另一方面也會造成分類困難。

不要搞Common,Core這些東西。每家公司的架構程式碼庫裡面,最噁心的一定是這兩個名字命名的資料夾,我這麼說一定不會錯。不要開Common,Core這樣的資料夾,開了之後後來者一定會把這個地方搞得一團糟,最終變成Common也不Common,Core也不Core。要記住,架構是不斷成長的,是會不斷變化的。不是每次成長每次變化,都是由你去實現的。如果真有什麼東西特別小,那就索性為了他單獨開闢一個模組就好了,小就小點,關鍵是要有序。

不用文件,或很少文件,就能讓業務方上手

誰特麼會去看文件啊,業務方他們已經被產品經理逼得很忙了。所以你要儘可能讓你的API名字可讀性強,對於iOS來說,objc這門語言的特性把這個做到了極致,函式名長就長一點,不要緊。

好的函式名:
– (NSDictionary *)exifDataOfImage:(UIImage *)image atIndexPath:(NSIndexPath *)indexPath;

壞的函式名:
– (id)exifData:(UIImage *)image position:(id)indexPath callback:(id<ErrorDelegate>)delegate;

為什麼壞?
1. 不要直接返回id或者傳入id,實在不行,用id<protocol>也比id好。如果連這個都做不到,你要好好考慮你的架構是不是有問題。
2. 要告知業務方要傳的東西是什麼,比如要傳Image,那就寫上ofImage。如果要傳位置,那就要寫上IndexPath,而不是用position這麼籠統的東西
3. 沒有任何理由要把delegate作為引數傳進去,一定不會有任何情況不得不這麼做的。而且delegate這個引數根本不是這個函式要解決的問題的充要條件,如果你發現你不得不這麼做,那一定是架構有問題!

思路和方法要統一,儘量不要多元

解決一個問題會有很多種方案,但是一旦確定了一種方案,就不要在另一個地方採用別的方案了。也就是做架構的時候,你得時刻記住當初你決定要處理這樣型別的問題的方案是什麼,以及你的初衷是什麼,不要搖擺不定。

另外,你當初設立這個模組一定是有想法有原因的,要記錄下你的解決思路,不要到時候換個地方你又靈光一現啥的,引入了其他方案,從而導致異構。

要是一個框架裡面解決同一種類似的問題有各種五花八門的方法或者類,我覺得做這個架構的架構師一定是自己都沒想清楚就開始搞了。

沒有橫向依賴,萬不得已不出現跨層訪問

沒有橫向依賴是很重要的,這決定了你將來要對這個架構做修補所需要的成本有多大。要做到沒有橫向依賴,這是很考驗架構師的模組分類能力和是否熟悉業務的。

跨層訪問是指資料流向了跟自己沒有對接關係的模組。有的時候跨層訪問是不可避免的,比如網路底層裡面訊號從2G變成了3G變成了4G,這是有可能需要跨層通知到View的。但這種情況不多,一旦出現就要想盡一切辦法在本層搞定或者交給上層或者下層搞定,儘量不要出現跨層的情況。跨層訪問同樣也會增加耦合度,當某一層需要整體替換的時候,牽涉面就會很大。

對業務方該限制的地方有限制,該靈活的地方要給業務方創造靈活實現的條件

把這點做好,很依賴於架構師的經驗。架構師必須要有能力區分哪些情況需要限制靈活性,哪些情況需要創造靈活性。比如對於Core Data技術棧來說,ManagedObject理論上是可以出現在任何地方的,那就意味著任何地方都可以修改ManagedObject,這就導致ManagedObjectContext在同步修改的時候把各種不同來源的修改同步進去。這時候就需要限制靈活性,只對外公開一個修改介面,不暴露任何ManagedObject在外面。

如果是設計一個ABTest相關的API的時候,我們又希望增加它的靈活性。使得業務方不光可以通過Target-Action的模式實現ABtest,也要可以通過Block的方式實現ABTest,要儘可能滿足靈活性,減少業務方的使用成本。

易測試易擴充

老生常談,要實現易測試易擴充,那就要提高模組化程度,儘可能減少依賴關係,便於mock。另外,如果是高度模組化的架構,擴充起來將會是一件非常容易的事情。

保持一定量的超前性

這一點能看出架構師是否關注行業動態,是否能準確把握技術走向。保持適度的技術上的超前性,能夠使得你的架構更新變得相對輕鬆。

另外,這裡的超前性也不光是技術上的,還有產品上的。誰說架構師就不需要跟產品經理打交道了,沒事多跟產品經理聊聊天,聽聽他對產品未來走向的暢想,你就可以在合理的地方為他的暢想留一條路子。同時,在創業公司的環境下,很多產品需求其實只是為了趕產品進度而產生的妥協方案,最後還是會轉到正軌的。這時候業務方可以不實現轉到正規的方案,但是架構這邊,是一定要為這種可預知的改變做準備的。

介面少,介面引數少

越少的介面越少的引數,就能越降低業務方的使用成本。當然,充要條件還是要滿足的,如何在滿足充要條件的情況下儘可能地減少介面和引數數量,這就能看出架構師的功力有多深厚了。

高效能

為什麼高效能排在最後一位?

高效能非常重要,但是在客戶端架構中,它不是第一考慮因素。原因有下:

  • 客戶端業務變化非常之快,做架構時首要考慮因素應當是便於業務方快速滿足產品需求,因此需要儘可能提供簡單易用效果好的介面給業務方,而不是提供高效能的介面給業務方。
  • 蘋果平臺的效能非常之棒,正常情況下很少會出現由於效能不夠導致的使用者體驗問題。
  • 蘋果平臺的優化手段相對有限,甚至於有些時候即便動用了無所不用其極的手段乃至不擇手段犧牲了穩定性,效能提高很有可能也只不過是100ms到90ms的差距。10%的效能提升對於服務端來說很不錯了,因為服務端動不動就是幾十萬上百萬的訪問量,幾十萬上百萬個10ms是很可觀的。但是對於客戶端的使用者來說,他無法感知這10ms的差別,如果從10s優化成9s使用者還是有一定感知的,但是100ms變90ms,我覺得吧,還是別折騰了。

但是!不重要不代表用不著去做,關於效能優化的東西,我會對應放到各系列文章裡面去。比如網路層優化,那就會在網路層方案的那篇文章裡面去寫,對應每層架構都有每層架構的不同優化方案,我都會在各自文章裡面一一細說。

2015-4-2 11:28 補: 關於架構分層?

昨晚上志豪看了這篇文章之後說,看到你這個題目本來我是期望看到關於架構分層相關的東西的,但是你沒寫。

嗯,確實沒寫,當時沒寫的原因是感覺這個沒什麼好寫的。前面談論到架構的方法的時候,關於問題分類分模組這一步時,架構分層也屬於這一部分,給我一筆帶過了。

既然志豪提出來了這個問題,我想可能大家關於這個也會有一些想法和問題,那麼我就在這兒講講吧。

其實分層這種東西,真沒啥技術含量,全憑架構師的經驗和素質。

我們常見的分層架構,有三層架構的:展現層、業務層、資料層。也有四層架構的:展現層、業務層、網路層、本地資料層。這裡說三層四層,跟TCP/IP所謂的五層或者七層不是同一種概念。再具體說就是:你這個架構在邏輯上是幾層那就幾層,具體每一層叫什麼,做什麼,沒有特定的規範。這主要是針對模組分類而言的。

也有說MVC架構,MVVM架構的,這種層次劃分,主要是針對資料流動的方向而言的。

在實際情況中,針對資料流動方向做的設計和針對模組分類做的設計是會放在一起的,也就是說,一個MVC架構可以是四層:展現層、業務層、網路層、本地資料層。

那麼,為什麼我要說這個?

大概在五六年前,業界很流行三層架構這個術語。然後各種文件資料漫天的三層架構,並且喜歡把它與MVC放在一起說,MVC三層架構/三層架構MVC,以至於很多人就會認為三層架構就是MVCMVC就是三層架構。其實不是的。三層架構裡面其實沒有Controller的概念,而且三層架構描述的側重點是模組之間的邏輯關係。MVCController的概念,它描述的側重點在於資料流動方向。

好,為什麼流行起來的是三層架構,而不是四層架構五層架構

因為所有的模組角色只會有三種:資料管理者資料加工者資料展示者,意思也就是,籠統說來,軟體只會有三層,每一層扮演一個角色。其他的第四層第五層,一般都是這三層裡面的其中之一分出來的,最後都能歸納進這三層的某一層中去,所以用三層架構來描述就比較普遍。

那麼我們怎麼做分層?

應該如何做分層,不是在做架構的時候一開始就考慮的問題。雖然我們要按照自頂向下的設計方式來設計架構,但是一般情況下不適合直接從三層開始。一般都是先確定所有要解決的問題,先確定都有哪些模組,然後再基於這些模組再往下細化設計。然後再把這些列出來的問題和模組做好分類。分類之後不出意外大多數都是三層。如果發現某一層特別龐大,那就可以再拆開來變成四層,變成五層。


舉個例子:你要設計一個即時通訊的服務端架構,怎麼分層?

記住,不要一上來就把三層架構的規範套上去,這樣做是做不出好架構的。

你要先確定都需要解決哪些問題。這裡只是舉例子,我隨意列出一點意思意思就好了:

  1. 要解決使用者登入、退出的問題
  2. 解決不同使用者間資料交流的問題
  3. 解決使用者資料儲存的問題
  4. 如果是多臺伺服器的叢集,就要解決使用者連線的定址問題

解決第一個問題需要一個連結管理模組,連結管理模組一般是通過連結池來實現。 解決第二個問題需要有一個資料交換模組,從A接收來的資料要給到B,這個事情由這個模組來做。 解決第三個問題需要有個資料庫,如果是服務於大量使用者,那麼就需要一個緩衝區,只有當需要儲存的資料達到一定量時才執行寫操作。 解決第四個問題可以有幾種解決方案,一個是叢集中有那麼幾臺伺服器作為尋路伺服器,所有尋路的服務交給那幾臺去做,那麼你需要開發一個尋路服務的Daemon。或者用廣播方式尋路,但如果尋路頻次非常高,會造成叢集內部網路負載特別大。這是你要權衡的地方,目前流行的思路是去中心化,那麼要解決網路負載的問題,你就可以考慮配置一個快取。

於是我們有了這些模組:

連結管理、資料交換、資料庫及其配套模組、尋路模組

做到這裡還遠遠沒有結束,你要繼續針對這四個模組繼續往下細分,直到足夠小為止。但是這裡只是舉例子,所以就不往下深究了。

另外,我要提醒你的是,直到這時,還是跟幾層架構毫無關係的。當你把所有模組都找出來之後,就要開始整理你的這些模組,很有可能架構圖就是這樣:

然後這些模組分完之後你看一下圖,嗯,1、2、3,一共三層,所以那就是三層架構啦。在這裡最消耗腦力最考驗架構師功力的地方就在於:找到所有需要的模組把模組放在該放的地方

這個例子側重點在於如何分層,效能優化、資料互動規範和包協議、資料採集等其他一系列必要的東西都沒有放進去,但看到這裡,相信你應該瞭解架構師是怎麼對待分層問題的了吧?

對的,答案就是沒有分層。所謂的分層都是出架構圖之後的事情了。所以你看別的架構師在演講的時候,上來第一句話差不多都是:”這個架構分為以下幾層…”。但考慮分層的問題的時機絕對不是一開始就考慮的。另外,模組一定要把它設計得獨立性強,這其實是門藝術活。

另外,這雖然是服務端架構,但是思路跟客戶端架構是一樣的,側重點不同罷了。之所以不拿客戶端架構舉例子,是因為這方面的客戶端架構蘋果已經幫你做好了絕大部分事情,沒剩下什麼值得說的了。

2015-4-5 12:15 補:關於Common資料夾?

評論區MatrixHero提到一點:

關於common資料夾的問題,僅僅是資料夾而已,別無他意。如果後期維護出了程式碼混亂可能是因為,和伺服器溝通協議不統一,或程式碼review不及時。應該有專人維護公共類。

這是針對我前面提出的不要Common,不要Core而言的,為什麼我建議大家不要開Common資料夾?我打算分幾種情況給大家解釋一下。

一般情況下,我們都會有一些屬於這個專案的公共類,比如取定位座標,比如影像處理。這些模組可能非常小,就h和m兩個檔案。單獨拎出來成為一個模組感覺不夠格,但是又不屬於其他任何一個模組。於是大家很有可能就會把它們放入Common裡面,我目前見到的大多數工程和大多數文件裡面的程式碼都喜歡這麼做。在當時來看,這麼做看不出什麼問題,但關鍵在於:軟體是有生命,會成長的。當時分出來的小模組,很有可能會隨著業務的成長,逐漸發展成大模組,發展成大模組後,可以再把它從Common移出來單獨成立一個模組。這個在理論上是沒有任何問題的,然而在實際操作過程中,工程師在拓張這個小模組的時候,不太容易會去考慮橫向依賴的問題,因為當時這些模組都在Common裡面,直接進行互相依賴是非常符合直覺的,而且也不算是不遵守規範。然而要注意的是,這才是Commom程式碼混亂的罪魁禍首,Common資料夾縱容了不精心管理依賴的做法。當Common裡面的模組依賴關係變得複雜,再想要移出來單獨成立一個模組,就不是當初設定Common時想的等規模大了再移除也不遲那麼簡單了。

另外,Common有的時候也不僅僅是一個資料夾。

在使用Cocoapods來管理專案庫的時候,Common往往就是一個pod。這個pod裡面會有A/B/C/D/E這些函式集或小模組。如果要新開一個app或者Demo,勢必會使用到Common這個pod,這麼做,往往會把不需要包含的程式碼也包含進去,我對專案有高度潔癖,這種情況會讓我覺得非常不舒服。


舉個例子:早年安居客的app還不是集齊所有新房二手房租房業務的。當你剛開始寫新房這個app的時候,建立了一個Common這個pod,這裡麵包含了一些對於新房來說比較Common的程式碼,也包含了對於這個app來說比較Common的程式碼。過了半年或者一年,你要開始二手房這個app,我覺得大多數人都會選擇讓二手房也包含這個Common,於是這個Common很有可能自己走上另一條發展的道路。等到了租房這個業務要開app的時候,Common已經非常之龐大,相信這時候的你也不會去想整理Common的事情了,先把租房搞定,於是Common最終就變成了一坨屎。

就對於上面的例子來說,還有一個要考慮的是,分出來的三個業務很有可能會有三個Common,假設三個Common裡面都有公共的功能,交給了三個團隊去打理,如果遇到某個子模組需要升級,那麼三個Common裡面的這個子模組都要去同步升級,這是個很不效率的事情。另外,很有可能三個Common到最後發展成彼此不相容,但是程式碼相似度非常之高,這個在架構上,是屬於分類條理不清

就在去年年中的時候,安居客決定將三個業務歸併到同一個App。好了,如果你是架構師,面對這三個Common,你打算怎麼辦?要想最快出成果,那就只好忍受程式碼冗餘,趕緊先把架子搭起來再說,否則你面對的就是剪不斷理還亂的Common。此時Common就已經很無奈地變成一坨屎了。這樣的Common,你自己說不定也搞不清楚它裡面到底都有些什麼了,交給任何一個人去打理,他都不敢做徹底的整理的。


還有就是,Common本身就是一個粒度非常大的模組。在阿里這樣大規模的團隊中,即便新開一個業務,都需要在整個app的環境下開發,為什麼?因為模組拆分粒度不夠,要想開一個新業務,必須把其他業務的程式碼以及依賴全部拉下來,然後再開新入口,你的新業務才能進行正常的程式碼編寫和除錯。然而你的新業務其實只依賴首頁入口、網路庫等這幾個小模組,不需要依賴其他那麼多的跟你沒關係的業務。現在每次開啟天貓的專案,我都要等個兩三分鐘,這非常之蛋疼。

但是大家真的不知道這個原因嗎?知道了這個原因,為什麼沒人去把這些粒度不夠細的模組整理好?在我看來,這件事沒人敢做。

  1. 原來大家用的好好的,手段爛就爛一點,你改了你能保證不出錯?
  2. 這麼複雜的東西,短期之內你肯定搞不好,任務量和工時都不好估,你leader會覺得你在騙工時玩自己的事情。
  3. 就算你搞定了,QA這邊肯定再需要做一次全面的迴歸測試,任務量極大,難以說服他們配合你的工作。

花這麼大的成本只是為了減少開啟專案時候等待IDE開啟時的那幾分鐘時間?我想如果我是你leader,我也應該不會批准你做這樣的事情的。所以,與其到了後面吃這個苦頭,不如一開始做架構的時候就不要設定Common,到後面就能省力很多。架構師的工作為什麼是功在當代利在千秋,架構師的素質為什麼對團隊這麼重要?我覺得這裡就是一個最好的體現。


簡而言之,不建議開Common的原因如下:

  1. Common不僅僅是一個資料夾,它也會是一個Pod。不管是什麼,在Common裡面很容易形成錯綜複雜的小模組依賴,在模組成長過程中,會縱容工程師不注意依賴的管理,乃至於將來如果要將模組拆分出去,會非常的困難。
  2. Common本身與細粒度模組設計的思想背道而馳,屬於一種不合適的偷懶手段,在將來業務拓張會成為阻礙。
  3. 一旦設定了Common,就等於給地獄之門開啟了一個小縫,每次業務迭代都會有一些不太好分類的東西放入Common,這就給維護Common的人帶來了非常大的工作量,而且這些工作量全都是體力活,非常容易出錯。

那麼,不設Common會帶來哪些好處?

  1. 強迫工程師在業務拓張的時候將依賴管理的事情考慮進去,讓模組在一開始發展的時候就有自己的土壤,成長空間和靈活度非常大。
  2. 減少各業務模組或者Demo的體積,不需要的模組不會由於Common的存在而包含在內。
  3. 可維護性大大提高,模組升級之後要做的同步工作非常輕鬆,解放了那個苦逼的Common維護者,更多的時間可以用在更實質的開發工作上。
  4. 符合細粒度模組劃分的架構思想。

Common的好處只有一個,就是前期特別省事兒。然而它的壞處比好處要多太多。不設定Common,再小的模組再小的程式碼也單獨拎出來,最多就是Podfile裡面要多寫幾行,多寫幾行最多隻花費幾分鐘。但若要消除Common所帶來的罪孽,不是這幾分鐘就能搞定的事情。既然不用Common的好處這麼多,那何樂而不為呢?

假設將來你的專案中有一個類是用來做Location的,哪怕只有兩個檔案,也給他開一個模組就叫Location。如果你的專案中有一個類是用來做ImageProcess的,那也開一個模組就叫ImageProcess。不要都放到Common裡面去,將來你再開新的專案或者新的業務,用Location就寫依賴Location,用ImageProcess就寫依賴ImageProcess,不要再依賴Common了,這樣你的專案也好管理,管理Common的那個人日子過得也輕鬆(這個人其實都可以不需要了,把他的工資加到你頭上不是更好?:D),將來要升級,顧慮也少。

結束

一下子挖了個大坑,在開篇裡扯了一些淡。

嗯,乾貨會在後續的系列文章裡面撲面而來的!

相關文章