iOS應用架構談 開篇
iOS應用架構談 view層的組織和調(diào)用方案
iOS應用架構談 網(wǎng)絡層設計方案
iOS應用架構談 動態(tài)部署方案
iOS應用架構談 本地持久化方案
緣由
之前安居客iOS app的第二版架構大部分內(nèi)容是我做的笋妥,期間有總結了一些經(jīng)驗。在將近一年之后编丘,前同事zzz在微信朋友圈上發(fā)了一個問題:假如問你一個iOS or Android app的架構锤灿,你會從哪些方面來說呢党巾?
當時看到這個問題正好在乘公車回家的路上肥卡,閑來無聊就答了一把。在zzz在微信朋友圈上追問了幾個問題之后告材,我覺得有必要開個博客專門來講講一些個人見解坤次。
其實對于iOS客戶端應用的架構來說,復雜度不亞于服務端斥赋,但側(cè)重點和入手點卻跟服務端不太一樣缰猴。比如客戶端應用就不需要考慮類似C10K的問題,正常的app就根本不需要考慮疤剑。
這系列文章我會主要專注在iOS應用架構方面滑绒,很多方案也是基于iOS技術棧的特點而建立的闷堡。因為我個人不是很喜歡寫Java,所以Android這邊的我就不太了解了疑故。如果你是Android開發(fā)者杠览,你可以側(cè)重看我提出的一些架構思想,畢竟不管做什么纵势,思路是相通的踱阿,實現(xiàn)手段不同罷了。
當我們討論客戶端應用架構的時候钦铁,我們在討論什么软舌?
其實市面上大部分應用不外乎就是顛過來倒過去地做以下這些事情:
簡單來說就是調(diào)API,展示頁面育瓜,然后跳轉(zhuǎn)到別的地方再調(diào)API葫隙,再展示頁面。
那這特么有毛好架構的躏仇?
非也恋脚,非也。 ---- 包不同 《天龍八部》
App確實就是主要做這些事情焰手,但是支撐這些事情的基礎糟描,就是做架構要考慮的事情。
調(diào)用網(wǎng)絡API
頁面展示
數(shù)據(jù)的本地持久化
動態(tài)部署方案
上面這四大點书妻,稍微細說一下就是:
如何讓業(yè)務開發(fā)工程師方便安全地調(diào)用網(wǎng)絡API船响?然后盡可能保證用戶在各種網(wǎng)絡環(huán)境下都能有良好的體驗?
頁面如何組織躲履,才能盡可能降低業(yè)務方代碼的耦合度见间?盡可能降低業(yè)務方開發(fā)界面的復雜度,提高他們的效率工猜?
當數(shù)據(jù)有在本地存取的需求的時候米诉,如何能夠保證數(shù)據(jù)在本地的合理安排?如何盡可能地減小性能消耗篷帅?
iOS應用有審核周期史侣,如何能夠通過不發(fā)版本的方式展示新的內(nèi)容給用戶?如何修復緊急bug魏身?
上面幾點是針對App說的惊橱,下面還有一些是針對團隊說的:
收集用戶數(shù)據(jù),給產(chǎn)品和運營提供參考
合理地組織各業(yè)務方開發(fā)的業(yè)務模塊箭昵,以及相關基礎模塊
每日app的自動打包税朴,提供給QA工程師的測試工具
一時半會兒我還是只能想到上面這三點,事實上應該還會有很多,想不起來了正林。
所以當我們討論客戶端應用架構的時候茧跋,我們討論的差不多就是這些問題。
這系列文章要回答那些問題卓囚?
這系列文章主要是回答以下這些問題:
網(wǎng)絡層設計方案瘾杭?設計網(wǎng)絡層時要考慮哪些問題?對網(wǎng)絡層做優(yōu)化的時候哪亿,可以從哪些地方入手粥烁?
頁面的展示、調(diào)用和組織都有哪些設計方案蝇棉?我們做這些方案的時候都要考慮哪些問題讨阻?
本地持久化層的設計方案都有哪些?優(yōu)劣勢都是什么篡殷?不同方案間要注意的問題分別都是什么钝吮?
要實現(xiàn)動態(tài)部署,都有哪些方案板辽?不同方案之間的優(yōu)劣點奇瘦,他們的側(cè)重點?
本文要回答那些問題劲弦?
上面細分出來的四個問題耳标,我會分別在四篇文章里面寫。那么這篇文章就是來講一些通識啥的邑跪,也是開個坑給大家討論通識問題的次坡。
架構設計的方法
所有事情最難的時候都是開始做的時候,當你開始著手設計并實現(xiàn)某一層的架構乃至整個app的架構的時候画畅,很有可能會出現(xiàn)暫時的無從下手的情況砸琅。以下方法論是我這些年總結出來的經(jīng)驗,每個架構師也一定都有一套自己的方法論轴踱,但一樣的是症脂,不管你采用什么方法,全局觀寇僧、高度的代碼審美能力摊腋、靈活使用各種設計模式一定都是貫穿其中的沸版。歡迎各位在評論區(qū)討論嘁傀。
第一步:搞清楚要解決哪些問題,并找到解決這些問題的充要條件视粮。
你必須得清楚你要做什么细办,業(yè)務方希望要什么。而不是為了架構而架構,也不是為了體驗新技術而改架構方案笑撞。以前是MVC岛啸,最近流行MVVM,如果過去的MVC是個好架構茴肥,沒什么特別大的缺陷坚踩,就不要推倒然后搞成MVVM。
關于充要條件我也要說明一下瓤狐,有的時候系統(tǒng)提供的函數(shù)是需要額外參數(shù)的瞬铸,比如read函數(shù)。還有翻頁的時候础锐,當前頁碼也是充要條件嗓节。但對于業(yè)務方來說,這些充要條件還能夠再縮減皆警。
比如read拦宣,需要給出file descriptor,需要給出buf信姓,需要給出size鸵隧。但是對于業(yè)務方來說,充要條件就只要file descriptor就夠了意推。再比如翻頁掰派,其實業(yè)務方并不需要記錄當前頁號,你給他暴露一個loadNextPage這樣的方法就夠了左痢。
搞清楚對于業(yè)務方而言的真正充要條件很重要靡羡!這決定了你的架構是否足夠易用。另外俊性,傳的參數(shù)越少略步,耦合度相對而言就越小,你替換模塊或者升級模塊所花的的代價就越小定页。
第二步:問題分類趟薄,分模塊
這個不用多說了吧。
第三步:搞清楚各問題之間的依賴關系典徊,建立好模塊交流規(guī)范并設計模塊杭煎。
關鍵在于建立一套統(tǒng)一的交流規(guī)范。這一步很能夠體現(xiàn)架構師在軟件方面的價值觀卒落,雖然存在一定程度上的好壞優(yōu)劣(比如胖Model和瘦Model)羡铲,但既然都是架構師了,基本上是不會設計出明顯很爛的方案的儡毕,除非這架構師還不夠格也切。所以這里是架構師價值觀輸出的一個窗口,從這一點我們是能夠看出架構師的素質(zhì)的。
另外要注意的是雷恃,一定是建立一套統(tǒng)一的交流規(guī)范疆股,不是兩套,不是多套倒槐。你要堅持你的價值觀旬痹,不要搖擺不定。要是搞出各種五花八門的規(guī)范出來讨越,一方面有不切實際的炫技嫌疑唱凯,另一方面也會帶來后續(xù)維護的災難。
第四步:推演預測一下未來可能的走向谎痢,必要時添加新的模塊磕昼,記錄更多的基礎數(shù)據(jù)以備未來之需。
很多稱職的架構師都會在這時候考慮架構未來的走向节猿,以及考慮做完這一輪架構之后票从,接下來要做的事情。一個好的架構雖然是功在當代利在千秋的工程滨嘱,但絕對不是一個一勞永逸的工程峰鄙。軟件是有生命的,你做出來的架構決定了這個軟件它這一生是坎坷還是幸福太雨。
第五步:先解決依賴關系中最基礎的問題吟榴,實現(xiàn)基礎模塊,然后再用基礎模塊堆疊出整個架構囊扳。
這一步也是驗證你之前的設計是否合理的一步吩翻,隨著這一步的推進,你很有可能會遇到需要對架構進行調(diào)整的情況锥咸。這個階段一定要吹毛求疵高度負責地去開發(fā)狭瞎,不要得過且過,發(fā)現(xiàn)架構有問題就及時調(diào)整搏予。否則以后調(diào)整的成本就非常之大了熊锭。
第六步:打點,跑單元測試雪侥,跑性能測試碗殷,根據(jù)數(shù)據(jù)去優(yōu)化對應的地方。
你得用這些數(shù)據(jù)去向你的boss邀功速缨,你也得用這些數(shù)據(jù)去不斷調(diào)整你的架構锌妻。
總而言之就是要遵循這些原則:自頂向下設計(1,2鸟廓,3从祝,4步),自底向下實現(xiàn)(5)引谜,先測量牍陌,后優(yōu)化(6)。
什么樣的架構師是好架構師员咽?
每天都在學習毒涧,新技術新思想上手速度快,理解速度快贝室。
做不到這點契讲,你就是碼農(nóng)。
業(yè)務出身滑频,或者至少非常熟悉公司所處行業(yè)或者本公司的業(yè)務捡偏。
做不到這點,你就是運維峡迷。
熟悉軟件工程的各種規(guī)范银伟,踩過無數(shù)坑。不會為了完成需求不擇手段绘搞,不推崇quick & dirty彤避。
做不到這點,你比較適合去競爭對手那兒當工程師夯辖。
及時承認錯誤琉预,不要覺得承認錯誤會有損你架構師的身份。
做不到這點蒿褂,公關行業(yè)比較適合你圆米。
不為了炫技而炫技
做不到這點,你就是高中編程愛好者啄栓。
精益求精
做不到這點榨咐,(我想了好久,但我還是不知道你適合去干什么谴供。)
什么樣的架構叫好架構块茁?
代碼整齊,分類明確桂肌,沒有common数焊,沒有core
不用文檔,或很少文檔崎场,就能讓業(yè)務方上手
思路和方法要統(tǒng)一佩耳,盡量不要多元
沒有橫向依賴,萬不得已不出現(xiàn)跨層訪問
對業(yè)務方該限制的地方有限制谭跨,該靈活的地方要給業(yè)務方創(chuàng)造靈活實現(xiàn)的條件
易測試干厚,易拓展
保持一定量的超前性
接口少李滴,接口參數(shù)少
高性能
以上是我判斷一個架構是不是好架構的標準,這是根據(jù)重要性來排列的蛮瞄∷鳎客戶端架構跟服務端架構要考慮的問題和側(cè)重點是有一些區(qū)別的。下面我會針對每一點詳細講解一下:
代碼整齊挂捅,分類明確芹助,沒有common,沒有core
代碼整齊是每一個工程師的基本素質(zhì)闲先,先不說你搞定這個問題的方案有多好状土,解決速度有多快,如果代碼不整齊伺糠,一切都白搭蒙谓。因為你的代碼是要給別人看的,你自己也要看训桶。如果哪一天架構有修改彼乌,正好改到這個地方,你很容易自己都看不懂渊迁。另外慰照,破窗理論提醒我們,如果代碼不整齊分類不明確琉朽,整個架構會隨著一次一次的拓展而越來越混亂毒租。
分類明確的字面意思大家一定都了解,但還有一個另外的意思箱叁,那就是:不要讓一個類或者一個模塊做兩種不同的事情墅垮。如果有類或某模塊做了兩種不同的事情,一方面不適合未來拓展耕漱,另一方面也會造成分類困難算色。
不要搞Common,Core這些東西螟够。每家公司的架構代碼庫里面灾梦,最惡心的一定是這兩個名字命名的文件夾,我這么說一定不會錯妓笙。不要開Common昌渤,Core這樣的文件夾旷赖,開了之后后來者一定會把這個地方搞得一團糟灼舍,最終變成Common也不Common崇摄,Core也不Core。要記住辈赋,架構是不斷成長的鲫忍,是會不斷變化的膏燕。不是每次成長每次變化,都是由你去實現(xiàn)的悟民。如果真有什么東西特別小坝辫,那就索性為了他單獨開辟一個模塊就好了,小就小點逾雄,關鍵是要有序阀溶。
不用文檔腻脏,或很少文檔鸦泳,就能讓業(yè)務方上手。
誰特么會去看文檔啊永品,業(yè)務方他們已經(jīng)被產(chǎn)品經(jīng)理逼得很忙了做鹰。所以你要盡可能讓你的API名字可讀性強,對于iOS來說鼎姐,objc這門語言的特性把這個做到了極致钾麸,函數(shù)名長就長一點,不要緊炕桨。
思路和方法要統(tǒng)一饭尝,盡量不要多元。
解決一個問題會有很多種方案献宫,但是一旦確定了一種方案钥平,就不要在另一個地方采用別的方案了。也就是做架構的時候姊途,你得時刻記住當初你決定要處理這樣類型的問題的方案是什么涉瘾,以及你的初衷是什么,不要搖擺不定捷兰。
另外立叛,你當初設立這個模塊一定是有想法有原因的,要記錄下你的解決思路贡茅,不要到時候換個地方你又靈光一現(xiàn)啥的秘蛇,引入了其他方案,從而導致異構顶考。
要是一個框架里面解決同一種類似的問題有各種五花八門的方法或者類彤叉,我覺得做這個架構的架構師一定是自己都沒想清楚就開始搞了。
沒有橫向依賴村怪,萬不得已不出現(xiàn)跨層訪問秽浇。
沒有橫向依賴是很重要的,這決定了你將來要對這個架構做修補所需要的成本有多大甚负。要做到?jīng)]有橫向依賴柬焕,這是很考驗架構師的模塊分類能力和是否熟悉業(yè)務的审残。
跨層訪問是指數(shù)據(jù)流向了跟自己沒有對接關系的模塊。有的時候跨層訪問是不可避免的斑举,比如網(wǎng)絡底層里面信號從2G變成了3G變成了4G搅轿,這是有可能需要跨層通知到View的。但這種情況不多富玷,一旦出現(xiàn)就要想盡一切辦法在本層搞定或者交給上層或者下層搞定璧坟,盡量不要出現(xiàn)跨層的情況∈昱常跨層訪問同樣也會增加耦合度雀鹃,當某一層需要整體替換的時候,牽涉面就會很大励两。
對業(yè)務方該限制的地方有限制黎茎,該靈活的地方要給業(yè)務方創(chuàng)造靈活實現(xiàn)的條件。
把這點做好当悔,很依賴于架構師的經(jīng)驗傅瞻。架構師必須要有能力區(qū)分哪些情況需要限制靈活性,哪些情況需要創(chuàng)造靈活性盲憎。比如對于Core Data技術棧來說嗅骄,ManagedObject理論上是可以出現(xiàn)在任何地方的,那就意味著任何地方都可以修改ManagedObject饼疙,這就導致ManagedObjectContext在同步修改的時候把各種不同來源的修改同步進去溺森。這時候就需要限制靈活性,只對外公開一個修改接口宏多,不暴露任何ManagedObject在外面儿惫。
如果是設計一個ABTest相關的API的時候,我們又希望增加它的靈活性伸但。使得業(yè)務方不光可以通過Target-Action的模式實現(xiàn)ABtest肾请,也要可以通過Block的方式實現(xiàn)ABTest,要盡可能滿足靈活性更胖,減少業(yè)務方的使用成本铛铁。
易測試易拓展
老生常談,要實現(xiàn)易測試易拓展却妨,那就要提高模塊化程度饵逐,盡可能減少依賴關系,便于mock彪标。另外倍权,如果是高度模塊化的架構,拓展起來將會是一件非常容易的事情捞烟。
保持一定量的超前性
這一點能看出架構師是否關注行業(yè)動態(tài)薄声,是否能準確把握技術走向当船。保持適度的技術上的超前性,能夠使得你的架構更新變得相對輕松默辨。
另外德频,這里的超前性也不光是技術上的,還有產(chǎn)品上的缩幸。誰說架構師就不需要跟產(chǎn)品經(jīng)理打交道了壹置,沒事多跟產(chǎn)品經(jīng)理聊聊天,聽聽他對產(chǎn)品未來走向的暢想表谊,你就可以在合理的地方為他的暢想留一條路子钞护。同時,在創(chuàng)業(yè)公司的環(huán)境下铃肯,很多產(chǎn)品需求其實只是為了趕產(chǎn)品進度而產(chǎn)生的妥協(xié)方案患亿,最后還是會轉(zhuǎn)到正軌的传蹈。這時候業(yè)務方可以不實現(xiàn)轉(zhuǎn)到正規(guī)的方案押逼,但是架構這邊,是一定要為這種可預知的改變做準備的惦界。
接口少挑格,接口參數(shù)少
越少的接口越少的參數(shù),就能越降低業(yè)務方的使用成本沾歪。當然漂彤,充要條件還是要滿足的,如何在滿足充要條件的情況下盡可能地減少接口和參數(shù)數(shù)量灾搏,這就能看出架構師的功力有多深厚了挫望。
高性能
為什么高性能排在最后一位?
高性能非常重要狂窑,但是在客戶端架構中媳板,它不是第一考慮因素。原因有下:
客戶端業(yè)務變化非常之快泉哈,做架構時首要考慮因素應當是便于業(yè)務方快速滿足產(chǎn)品需求蛉幸,因此需要盡可能提供簡單易用效果好的接口給業(yè)務方,而不是提供高性能的接口給業(yè)務方丛晦。
蘋果平臺的性能非常之棒奕纫,正常情況下很少會出現(xiàn)由于性能不夠?qū)е碌挠脩趔w驗問題。
蘋果平臺的優(yōu)化手段相對有限烫沙,甚至于有些時候即便動用了無所不用其極的手段乃至不擇手段犧牲了穩(wěn)定性匹层,性能提高很有可能也只不過是100ms到90ms的差距。10%的性能提升對于服務端來說很不錯了锌蓄,因為服務端動不動就是幾十萬上百萬的訪問量升筏,幾十萬上百萬個10ms是很可觀的仲器。但是對于客戶端的用戶來說,他無法感知這10ms的差別仰冠,如果從10s優(yōu)化成9s用戶還是有一定感知的乏冀,但是100ms變90ms,我覺得吧洋只,還是別折騰了辆沦。
但是!不重要不代表用不著去做识虚,關于性能優(yōu)化的東西肢扯,我會對應放到各系列文章里面去。比如網(wǎng)絡層優(yōu)化担锤,那就會在網(wǎng)絡層方案的那篇文章里面去寫蔚晨,對應每層架構都有每層架構的不同優(yōu)化方案,我都會在各自文章里面一一細說肛循。
2015-4-2 11:28 補: 關于架構分層铭腕?
昨晚上志豪看了這篇文章之后說,看到你這個題目本來我是期望看到關于架構分層相關的東西的多糠,但是你沒寫累舷。
嗯,確實沒寫夹孔,當時沒寫的原因是感覺這個沒什么好寫的被盈。前面談論到架構的方法的時候,關于問題分類分模塊這一步時搭伤,架構分層也屬于這一部分只怎,給我一筆帶過了。
既然志豪提出來了這個問題怜俐,我想可能大家關于這個也會有一些想法和問題身堡,那么我就在這兒講講吧。
其實分層這種東西佑菩,真沒啥技術含量盾沫,全憑架構師的經(jīng)驗和素質(zhì)。
我們常見的分層架構殿漠,有三層架構的:展現(xiàn)層赴精、業(yè)務層、數(shù)據(jù)層绞幌。也有四層架構的:展現(xiàn)層蕾哟、業(yè)務層、網(wǎng)絡層、本地數(shù)據(jù)層谭确。這里說三層帘营、四層,跟TCP/IP所謂的五層或者七層不是同一種概念逐哈。再具體說就是:你這個架構在邏輯上是幾層那就幾層芬迄,具體每一層叫什么,做什么昂秃,沒有特定的規(guī)范禀梳。這主要是針對模塊分類而言的。
也有說MVC架構肠骆,MVVM架構的算途,這種層次劃分,主要是針對數(shù)據(jù)流動的方向而言的蚀腿。
在實際情況中嘴瓤,針對數(shù)據(jù)流動方向做的設計和針對模塊分類做的設計是會放在一起的,也就是說莉钙,一個MVC架構可以是四層:展現(xiàn)層廓脆、業(yè)務層、網(wǎng)絡層胆胰、本地數(shù)據(jù)層狞贱。
那么刻获,為什么我要說這個蜀涨?
大概在五六年前,業(yè)界很流行三層架構這個術語蝎毡。然后各種文檔資料漫天的三層架構厚柳,并且喜歡把它與MVC放在一起說,MVC三層架構/三層架構MVC沐兵,以至于很多人就會認為三層架構就是MVC别垮,MVC就是三層架構。其實不是的扎谎。三層架構里面其實沒有Controller的概念碳想,而且三層架構描述的側(cè)重點是模塊之間的邏輯關系。MVC有Controller的概念毁靶,它描述的側(cè)重點在于數(shù)據(jù)流動方向胧奔。
好,為什么流行起來的是三層架構预吆,而不是四層架構或五層架構龙填?
因為所有的模塊角色只會有三種:數(shù)據(jù)管理者、數(shù)據(jù)加工者、數(shù)據(jù)展示者岩遗,意思也就是扇商,籠統(tǒng)說來,軟件只會有三層宿礁,每一層扮演一個角色案铺。其他的第四層第五層,一般都是這三層里面的其中之一分出來的梆靖,最后都能歸納進這三層的某一層中去红且,所以用三層架構來描述就比較普遍。
那么我們怎么做分層涤姊?
應該如何做分層暇番,不是在做架構的時候一開始就考慮的問題。雖然我們要按照自頂向下的設計方式來設計架構思喊,但是一般情況下不適合直接從三層開始壁酬。一般都是先確定所有要解決的問題,先確定都有哪些模塊恨课,然后再基于這些模塊再往下細化設計舆乔。然后再把這些列出來的問題和模塊做好分類。分類之后不出意外大多數(shù)都是三層剂公。如果發(fā)現(xiàn)某一層特別龐大希俩,那就可以再拆開來變成四層,變成五層纲辽。
舉個例子:你要設計一個即時通訊的服務端架構颜武,怎么分層?
記住拖吼,不要一上來就把三層架構的規(guī)范套上去鳞上,這樣做是做不出好架構的。
你要先確定都需要解決哪些問題吊档。這里只是舉例子篙议,我隨意列出一點意思意思就好了:
要解決用戶登錄、退出的問題
解決不同用戶間數(shù)據(jù)交流的問題
解決用戶數(shù)據(jù)存儲的問題
如果是多臺服務器的集群怠硼,就要解決用戶連接的尋址問題
解決第一個問題需要一個鏈接管理模塊鬼贱,鏈接管理模塊一般是通過鏈接池來實現(xiàn)。 解決第二個問題需要有一個數(shù)據(jù)交換模塊香璃,從A接收來的數(shù)據(jù)要給到B这难,這個事情由這個模塊來做。 解決第三個問題需要有個數(shù)據(jù)庫增显,如果是服務于大量用戶雁佳,那么就需要一個緩沖區(qū)脐帝,只有當需要存儲的數(shù)據(jù)達到一定量時才執(zhí)行寫操作。 解決第四個問題可以有幾種解決方案糖权,一個是集群中有那么幾臺服務器作為尋路服務器堵腹,所有尋路的服務交給那幾臺去做,那么你需要開發(fā)一個尋路服務的Daemon星澳【吻辏或者用廣播方式尋路,但如果尋路頻次非常高禁偎,會造成集群內(nèi)部網(wǎng)絡負載特別大腿堤。這是你要權衡的地方,目前流行的思路是去中心化如暖,那么要解決網(wǎng)絡負載的問題笆檀,你就可以考慮配置一個緩存。
于是我們有了這些模塊:
鏈接管理盒至、數(shù)據(jù)交換酗洒、數(shù)據(jù)庫及其配套模塊、尋路模塊
做到這里還遠遠沒有結束枷遂,你要繼續(xù)針對這四個模塊繼續(xù)往下細分樱衷,直到足夠小為止。但是這里只是舉例子酒唉,所以就不往下深究了矩桂。
另外,我要提醒你的是痪伦,直到這時侄榴,還是跟幾層架構毫無關系的。當你把所有模塊都找出來之后流妻,就要開始整理你的這些模塊牲蜀,很有可能架構圖就是這樣:
然后這些模塊分完之后你看一下圖,嗯绅这,1、2在辆、3证薇,一共三層,所以那就是三層架構啦匆篓。在這里最消耗腦力最考驗架構師功力的地方就在于:找到所有需要的模塊, 把模塊放在該放的地方
這個例子側(cè)重點在于如何分層浑度,性能優(yōu)化、數(shù)據(jù)交互規(guī)范和包協(xié)議鸦概、數(shù)據(jù)采集等其他一系列必要的東西都沒有放進去箩张,但看到這里,相信你應該了解架構師是怎么對待分層問題的了吧?
對的先慷,答案就是沒有分層饮笛。所謂的分層都是出架構圖之后的事情了。所以你看別的架構師在演講的時候论熙,上來第一句話差不多都是:"這個架構分為以下幾層..."福青。但考慮分層的問題的時機絕對不是一開始就考慮的。另外脓诡,模塊一定要把它設計得獨立性強无午,這其實是門藝術活。
另外祝谚,這雖然是服務端架構宪迟,但是思路跟客戶端架構是一樣的,側(cè)重點不同罷了交惯。之所以不拿客戶端架構舉例子踩验,是因為這方面的客戶端架構蘋果已經(jīng)幫你做好了絕大部分事情,沒剩下什么值得說的了商玫。
2015-4-5 12:15 補:關于Common文件夾箕憾?
評論區(qū)MatrixHero提到一點:
關于common文件夾的問題,僅僅是文件夾而已拳昌,別無他意袭异。如果后期維護出了代碼混亂可能是因為,和服務器溝通協(xié)議不統(tǒng)一炬藤,或代碼review不及時御铃。應該有專人維護公共類。
這是針對我前面提出的不要Common沈矿,不要Core而言的上真,為什么我建議大家不要開Common文件夾?我打算分幾種情況給大家解釋一下羹膳。
一般情況下睡互,我們都會有一些屬于這個項目的公共類,比如取定位坐標陵像,比如圖像處理就珠。這些模塊可能非常小,就h和m兩個文件醒颖。單獨拎出來成為一個模塊感覺不夠格妻怎,但是又不屬于其他任何一個模塊。于是大家很有可能就會把它們放入Common里面泞歉,我目前見到的大多數(shù)工程和大多數(shù)文檔里面的代碼都喜歡這么做逼侦。在當時來看匿辩,這么做看不出什么問題,但關鍵在于:軟件是有生命榛丢,會成長的铲球。當時分出來的小模塊,很有可能會隨著業(yè)務的成長涕滋,逐漸發(fā)展成大模塊睬辐,發(fā)展成大模塊后,可以再把它從Common移出來單獨成立一個模塊宾肺。這個在理論上是沒有任何問題的溯饵,然而在實際操作過程中,工程師在拓張這個小模塊的時候锨用,不太容易會去考慮橫向依賴的問題丰刊,因為當時這些模塊都在Common里面,直接進行互相依賴是非常符合直覺的增拥,而且也不算是不遵守規(guī)范啄巧。然而要注意的是,這才是Commom代碼混亂的罪魁禍首掌栅,Common文件夾縱容了不精心管理依賴的做法秩仆。當Common里面的模塊依賴關系變得復雜,再想要移出來單獨成立一個模塊猾封,就不是當初設置Common時想的等規(guī)模大了再移除也不遲那么簡單了澄耍。
另外,Common有的時候也不僅僅是一個文件夾晌缘。
在使用Cocoapods來管理項目庫的時候齐莲,Common往往就是一個pod。這個pod里面會有A/B/C/D/E這些函數(shù)集或小模塊磷箕。如果要新開一個app或者Demo选酗,勢必會使用到Common這個pod,這么做岳枷,往往會把不需要包含的代碼也包含進去芒填,我對項目有高度潔癖,這種情況會讓我覺得非常不舒服嫩舟。
舉個例子:早年安居客的app還不是集齊所有新房氢烘、二手房、租房業(yè)務的家厌。當你剛開始寫新房這個app的時候,創(chuàng)建了一個Common這個pod椎工,這里面包含了一些對于新房來說比較Common的代碼饭于,也包含了對于這個app來說比較Common的代碼蜀踏。過了半年或者一年,你要開始二手房這個app掰吕,我覺得大多數(shù)人都會選擇讓二手房也包含這個Common果覆,于是這個Common很有可能自己走上另一條發(fā)展的道路。等到了租房這個業(yè)務要開app的時候殖熟,Common已經(jīng)非常之龐大局待,相信這時候的你也不會去想整理Common的事情了,先把租房搞定菱属,于是Common最終就變成了一坨屎钳榨。
就對于上面的例子來說,還有一個要考慮的是纽门,分出來的三個業(yè)務很有可能會有三個Common薛耻,假設三個Common里面都有公共的功能,交給了三個團隊去打理赏陵,如果遇到某個子模塊需要升級饼齿,那么三個Common里面的這個子模塊都要去同步升級,這是個很不效率的事情蝙搔。另外缕溉,很有可能三個Common到最后發(fā)展成彼此不兼容,但是代碼相似度非常之高吃型,這個在架構上证鸥,是屬于分類條理不清。
就在去年年中的時候败玉,安居客決定將三個業(yè)務歸并到同一個App敌土。好了,如果你是架構師运翼,面對這三個Common返干,你打算怎么辦?要想最快出成果血淌,那就只好忍受代碼冗余矩欠,趕緊先把架子搭起來再說,否則你面對的就是剪不斷理還亂的Common悠夯。此時Common就已經(jīng)很無奈地變成一坨屎了癌淮。這樣的Common,你自己說不定也搞不清楚它里面到底都有些什么了沦补,交給任何一個人去打理乳蓄,他都不敢做徹底的整理的。
還有就是夕膀,Common本身就是一個粒度非常大的模塊虚倒。在阿里這樣大規(guī)模的團隊中美侦,即便新開一個業(yè)務,都需要在整個app的環(huán)境下開發(fā)魂奥,為什么菠剩?因為模塊拆分粒度不夠,要想開一個新業(yè)務耻煤,必須把其他業(yè)務的代碼以及依賴全部拉下來具壮,然后再開新入口,你的新業(yè)務才能進行正常的代碼編寫和調(diào)試哈蝇。然而你的新業(yè)務其實只依賴首頁入口棺妓、網(wǎng)絡庫等這幾個小模塊,不需要依賴其他那么多的跟你沒關系的業(yè)務÷蚋耄現(xiàn)在每次打開天貓的項目涧郊,我都要等個兩三分鐘,這非常之蛋疼眼五。
但是大家真的不知道這個原因嗎妆艘?知道了這個原因,為什么沒人去把這些粒度不夠細的模塊整理好看幼?在我看來批旺,這件事沒人敢做。
原來大家用的好好的诵姜,手段爛就爛一點汽煮,你改了你能保證不出錯?
這么復雜的東西棚唆,短期之內(nèi)你肯定搞不好暇赤,任務量和工時都不好估,你leader會覺得你在騙工時玩自己的事情宵凌。
就算你搞定了鞋囊,QA這邊肯定再需要做一次全面的回歸測試,任務量極大瞎惫,難以說服他們配合你的工作溜腐。
花這么大的成本只是為了減少開啟項目時候等待IDE打開時的那幾分鐘時間?我想如果我是你leader瓜喇,我也應該不會批準你做這樣的事情的挺益。所以,與其到了后面吃這個苦頭乘寒,不如一開始做架構的時候就不要設置Common望众,到后面就能省力很多。架構師的工作為什么是功在當代利在千秋,架構師的素質(zhì)為什么對團隊這么重要黍檩?我覺得這里就是一個最好的體現(xiàn)叉袍。
簡而言之始锚,不建議開Common的原因如下:
Common不僅僅是一個文件夾刽酱,它也會是一個Pod。不管是什么瞧捌,在Common里面很容易形成錯綜復雜的小模塊依賴棵里,在模塊成長過程中,會縱容工程師不注意依賴的管理姐呐,乃至于將來如果要將模塊拆分出去殿怜,會非常的困難。
Common本身與細粒度模塊設計的思想背道而馳曙砂,屬于一種不合適的偷懶手段头谜,在將來業(yè)務拓張會成為阻礙。
一旦設置了Common鸠澈,就等于給地獄之門打開了一個小縫柱告,每次業(yè)務迭代都會有一些不太好分類的東西放入Common,這就給維護Common的人帶來了非常大的工作量笑陈,而且這些工作量全都是體力活际度,非常容易出錯。
那么涵妥,不設Common會帶來哪些好處乖菱?
強迫工程師在業(yè)務拓張的時候?qū)⒁蕾嚬芾淼氖虑榭紤]進去,讓模塊在一開始發(fā)展的時候就有自己的土壤蓬网,成長空間和靈活度非常大窒所。
減少各業(yè)務模塊或者Demo的體積,不需要的模塊不會由于Common的存在而包含在內(nèi)帆锋。
可維護性大大提高吵取,模塊升級之后要做的同步工作非常輕松,解放了那個苦逼的Common維護者窟坐,更多的時間可以用在更實質(zhì)的開發(fā)工作上海渊。
符合細粒度模塊劃分的架構思想。
Common的好處只有一個哲鸳,就是前期特別省事兒臣疑。然而它的壞處比好處要多太多。不設置Common徙菠,再小的模塊再小的代碼也單獨拎出來讯沈,最多就是Podfile里面要多寫幾行,多寫幾行最多只花費幾分鐘。但若要消除Common所帶來的罪孽缺狠,不是這幾分鐘就能搞定的事情问慎。既然不用Common的好處這么多,那何樂而不為呢挤茄?
假設將來你的項目中有一個類是用來做Location的如叼,哪怕只有兩個文件,也給他開一個模塊就叫Location穷劈。如果你的項目中有一個類是用來做ImageProcess的笼恰,那也開一個模塊就叫ImageProcess。不要都放到Common里面去歇终,將來你再開新的項目或者新的業(yè)務社证,用Location就寫依賴Location,用ImageProcess就寫依賴ImageProcess评凝,不要再依賴Common了追葡,這樣你的項目也好管理,管理Common的那個人日子過得也輕松(這個人其實都可以不需要了奕短,把他的工資加到你頭上不是更好宜肉?:D),將來要升級篡诽,顧慮也少崖飘。
結束
一下子挖了個大坑,在開篇里扯了一些淡杈女。