標籤:
緣由
從事iOS工作一年多了,主要從事QQ錢包SDK開發和財付通app維護,隨著對業務的慢慢熟悉,最近在思考這兩款應用架構設計的思想,剛好昨天在裡看了一篇iOS大牛對終端應用架構的分享,乘熱打鐵,下面淺談下我對ios應用架構設計的理解,寫的不好或不對的地方,歡迎大家拍磚,我們一起來探討。
假如問你一個iOS or Android app的架構,你會從哪些方面來說呢?
不要急著給出你的答案,可以先在你的腦子裡思考3分鐘,再看下面我要講的內容。
其實對於iOS用戶端應用的架構來說,複雜度不亞於服務端,但側重點和入手點卻跟服務端不太一樣。比如用戶端應用就不需要考慮類似服務端大並發量的問題,正常的app就根本不需要考慮。因為我個人做Android的時間不長,如果你是Android開發人員,你可以側重看我提出的一些架構思想,畢竟不管做什麼,思路是相通的,實現手段不同罷了。
當我們討論用戶端應用架構的時候,我們在討論什嗎?
其實市面上大部分應用不外乎就是顛過來倒過去地做以下這些事情:
簡單來說就是調API,展示頁面,然後跳轉到別的地方再調API,再展示頁面。
App確實就是主要做這些事情,但是支撐這些事情的基礎,就是做架構要考慮的事情。
調用網路API
頁面展示
資料的本地持久化
動態部署方案
上面這四大點,稍微細說一下就是:
如何讓業務開發工程師方便安全地調用網路API?然後儘可能保證使用者在各種網路環境下都能有良好的體驗?
頁面如何組織,才能儘可能降低業務方代碼的耦合度?儘可能降低業務方開發介面的複雜度,提高他們的效率?
當資料有在本地存取的需求的時候,如何能夠保證資料在本地的合理安排?如何儘可能地減小效能消耗?
iOS應用有審核周期,如何能夠通過不發版本的方式展示新的內容給使用者?如何修複緊急bug?
上面幾點是針對App說的,下面還有一些是針對團隊說的:
所以當我們討論用戶端應用架構的時候,我們討論的差不多就是這些問題。
架構設計的方法
所有事情最難的時候都是開始做的時候,當你開始著手設計並實現某一層的架構乃至整個app的架構的時候,很有可能會出現暫時的無從下手的情況。不管你採用什麼方法,全域觀、高度的代碼審美能力、靈活使用各種設計模式一定都是貫穿其中的。
第一步:搞清楚要解決哪些問題,並找到解決這些問題的充要條件
你必須得清楚你要做什麼,業務方希望要什麼。而不是為了架構而架構,也不是為了體驗新技術而改架構方案。以前是MVC,最近流行MVVM,如果過去的MVC是個好架構,沒什麼特別大的缺陷,就不要推倒然後搞成MVVM。
搞清楚對於業務方而言的真正充要條件很重要!這決定了你的架構是否足夠易用。另外,傳的參數越少,耦合度相對而言就越小,你替換模組或者升級模組所花的的代價就越小。
第二步:問題分類,分模組
這個不用多說了吧。
第三步:搞清楚各問題之間的依賴關係,建立好模組交流規範並設計模組
關鍵在於建立一套統一的交流規範。要注意的是,一定是建立一套統一的交流規範,不是兩套,不是多套。你要堅持你的價值觀,不要搖擺不定。要是搞出各種五花八門的規範出來,一方面有不切實際的炫技嫌疑,另一方面也會帶來後續維護的災難。
第四步:推演預測一下未來可能的走向,必要時添加新的模組,記錄更多的基礎資料以備未來之需
很多稱職的架構師都會在這時候考慮架構未來的走向,以及考慮做完這一輪架構之後,接下來要做的事情。一個好的架構雖然是功在當代利在千秋的工程,但絕對不是一個一勞永逸的工程。軟體是有生命的,你做出來的架構決定了這個軟體它這一生是坎坷還是幸福。
第五步:先解決依賴關係中最基礎的問題,實現基礎模組,然後再用基礎模組堆疊出整個架構
這一步也是驗證你之前的設計是否合理的一步,隨著這一步的推進,你很有可能會遇到需要對架構進行調整的情況。這個階段一定要吹毛求疵高度負責地去開發,不要得過且過,發現架構有問題就及時調整。否則以後調整的成本就非常之大了。
第六步:打點,跑單元測試,跑效能測試,根據資料去最佳化對應的地方
你得用這些資料去向你的leader邀功,你也得用這些資料去不斷調整你的架構。
總而言之就是要遵循這些原則:自頂向下設計(1,2,3,4步),自底向上實現(5),先測量,後最佳化(6)。
什麼樣的架構是好架構?
代碼整齊,分類明確
不用文檔,或很少文檔,就能讓業務方上手
思路和方法要統一,盡量不要多元
沒有橫向依賴,萬不得已不出現跨層訪問
對業務方該限制的地方有限制,該靈活的地方要給業務方創造靈活實現的條件
易測試,易拓展
保持一定量的超前性
介面少,介面參數少
高效能
下面針對每一點詳細講解一下:
代碼整齊,分類明確,沒有common,沒有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,我覺得吧,還是別折騰了。
但是!不重要不代表用不著去做,關於效能最佳化這裡有很多學問,需要在工作中慢慢積累。
尾聲
看了上面的內容,再問一遍自己假如問有一個iOS or Android app的架構,會從哪些方面來思考呢?
iOS應用架構淺談