原文地址:iOS應(yīng)用架構(gòu)談 開(kāi)篇
市面上大部分應(yīng)用不外乎就是顛過(guò)來(lái)倒過(guò)去地做以下這些事情:
App確實(shí)就是主要做這些事情壹若,但是支撐這些事情的基礎(chǔ)承粤,就是做架構(gòu)要考慮的事情择葡。
- 調(diào)用網(wǎng)絡(luò)API
- 頁(yè)面展示
- 數(shù)據(jù)的本地持久化
- 動(dòng)態(tài)部署方案
上面這四大點(diǎn)肴茄,稍微細(xì)說(shuō)一下就是:
- 如何讓業(yè)務(wù)開(kāi)發(fā)工程師方便安全地調(diào)用網(wǎng)絡(luò)API纯丸?然后盡可能保證用戶在各種網(wǎng)絡(luò)環(huán)境下都能有良好的體驗(yàn)偏形?
- 頁(yè)面如何組織,才能盡可能降低業(yè)務(wù)方代碼的耦合度觉鼻?盡可能降低業(yè)務(wù)方開(kāi)發(fā)界面的復(fù)雜度俊扭,提高他們的效率?
- 當(dāng)數(shù)據(jù)有在本地存取的需求的時(shí)候坠陈,如何能夠保證數(shù)據(jù)在本地的合理安排萨惑?如何盡可能地減小性能消耗?
- iOS應(yīng)用有審核周期仇矾,如何能夠通過(guò)不發(fā)版本的方式展示新的內(nèi)容給用戶咒钟?如何修復(fù)緊急bug?
上面幾點(diǎn)是針對(duì)App說(shuō)的若未,下面還有一些是針對(duì)團(tuán)隊(duì)說(shuō)的:
- 收集用戶數(shù)據(jù),給產(chǎn)品和運(yùn)營(yíng)提供參考
- 合理地組織各業(yè)務(wù)方開(kāi)發(fā)的業(yè)務(wù)模塊倾鲫,以及相關(guān)基礎(chǔ)模塊
- 每日app的自動(dòng)打包粗合,提供給QA工程師的測(cè)試工具
主要需要思考以下這些問(wèn)題:
- 網(wǎng)絡(luò)層設(shè)計(jì)方案萍嬉?設(shè)計(jì)網(wǎng)絡(luò)層時(shí)要考慮哪些問(wèn)題?對(duì)網(wǎng)絡(luò)層做優(yōu)化的時(shí)候隙疚,可以從哪些地方入手壤追?
- 頁(yè)面的展示、調(diào)用和組織都有哪些設(shè)計(jì)方案供屉?我們做這些方案的時(shí)候都要考慮哪些問(wèn)題行冰?
- 本地持久化層的設(shè)計(jì)方案都有哪些??jī)?yōu)劣勢(shì)都是什么伶丐?不同方案間要注意的問(wèn)題分別都是什么悼做?
- 要實(shí)現(xiàn)動(dòng)態(tài)部署,都有哪些方案哗魂?不同方案之間的優(yōu)劣點(diǎn)肛走,他們的側(cè)重點(diǎn)?
架構(gòu)設(shè)計(jì)的方法
第一步:搞清楚要解決哪些問(wèn)題录别,并找到解決這些問(wèn)題的充要條件朽色。
你必須得清楚你要做什么,業(yè)務(wù)方希望要什么组题。而不是為了架構(gòu)而架構(gòu)葫男,也不是為了體驗(yàn)新技術(shù)而改架構(gòu)方案。以前是MVC崔列,最近流行MVVM梢褐,如果過(guò)去的MVC是個(gè)好架構(gòu),沒(méi)什么特別大的缺陷峻呕,就不要推倒然后搞成MVVM利职。
關(guān)于充要條件我也要說(shuō)明一下,有的時(shí)候系統(tǒng)提供的函數(shù)是需要額外參數(shù)的瘦癌,比如read函數(shù)猪贪。還有翻頁(yè)的時(shí)候,當(dāng)前頁(yè)碼也是充要條件讯私。但對(duì)于業(yè)務(wù)方來(lái)說(shuō)热押,這些充要條件還能夠再縮減。
比如read斤寇,需要給出file descriptor桶癣,需要給出buf,需要給出size娘锁。但是對(duì)于業(yè)務(wù)方來(lái)說(shuō)牙寞,充要條件就只要file descriptor就夠了。再比如翻頁(yè),其實(shí)業(yè)務(wù)方并不需要記錄當(dāng)前頁(yè)號(hào)间雀,你給他暴露一個(gè)loadNextPage這樣的方法就夠了悔详。
搞清楚對(duì)于業(yè)務(wù)方而言的真正充要條件很重要!這決定了你的架構(gòu)是否足夠易用惹挟。另外茄螃,傳的參數(shù)越少,耦合度相對(duì)而言就越小连锯,你替換模塊或者升級(jí)模塊所花的的代價(jià)就越小归苍。
第二步:?jiǎn)栴}分類,分模塊
這個(gè)不用多說(shuō)了吧运怖。
第三步:搞清楚各問(wèn)題之間的依賴關(guān)系拼弃,建立好模塊交流規(guī)范并設(shè)計(jì)模塊。
關(guān)鍵在于建立一套統(tǒng)一的交流規(guī)范驳规。這一步很能夠體現(xiàn)架構(gòu)師在軟件方面的價(jià)值觀肴敛,雖然存在一定程度上的好壞優(yōu)劣(比如胖Model和瘦Model),但既然都是架構(gòu)師了吗购,基本上是不會(huì)設(shè)計(jì)出明顯很爛的方案的医男,除非這架構(gòu)師還不夠格。所以這里是架構(gòu)師價(jià)值觀輸出的一個(gè)窗口捻勉,從這一點(diǎn)我們是能夠看出架構(gòu)師的素質(zhì)的镀梭。
另外要注意的是,一定是建立一套統(tǒng)一的交流規(guī)范踱启,不是兩套报账,不是多套。你要堅(jiān)持你的價(jià)值觀埠偿,不要搖擺不定透罢。要是搞出各種五花八門(mén)的規(guī)范出來(lái),一方面有不切實(shí)際的炫技嫌疑冠蒋,另一方面也會(huì)帶來(lái)后續(xù)維護(hù)的災(zāi)難羽圃。
第四步:推演預(yù)測(cè)一下未來(lái)可能的走向,必要時(shí)添加新的模塊抖剿,記錄更多的基礎(chǔ)數(shù)據(jù)以備未來(lái)之需朽寞。
很多稱職的架構(gòu)師都會(huì)在這時(shí)候考慮架構(gòu)未來(lái)的走向,以及考慮做完這一輪架構(gòu)之后斩郎,接下來(lái)要做的事情脑融。一個(gè)好的架構(gòu)雖然是功在當(dāng)代利在千秋的工程,但絕對(duì)不是一個(gè)一勞永逸的工程缩宜。軟件是有生命的肘迎,你做出來(lái)的架構(gòu)決定了這個(gè)軟件它這一生是坎坷還是幸福。
第五步:先解決依賴關(guān)系中最基礎(chǔ)的問(wèn)題,實(shí)現(xiàn)基礎(chǔ)模塊膜宋,然后再用基礎(chǔ)模塊堆疊出整個(gè)架構(gòu)窿侈。
這一步也是驗(yàn)證你之前的設(shè)計(jì)是否合理的一步,隨著這一步的推進(jìn)秋茫,你很有可能會(huì)遇到需要對(duì)架構(gòu)進(jìn)行調(diào)整的情況。這個(gè)階段一定要吹毛求疵高度負(fù)責(zé)地去開(kāi)發(fā)乃秀,不要得過(guò)且過(guò)肛著,發(fā)現(xiàn)架構(gòu)有問(wèn)題就及時(shí)調(diào)整。否則以后調(diào)整的成本就非常之大了跺讯。
第六步:打點(diǎn)枢贿,跑單元測(cè)試,跑性能測(cè)試刀脏,根據(jù)數(shù)據(jù)去優(yōu)化對(duì)應(yīng)的地方局荚。
你得用這些數(shù)據(jù)去向你的boss邀功,你也得用這些數(shù)據(jù)去不斷調(diào)整你的架構(gòu)愈污。
總而言之就是要遵循這些原則:自頂向下設(shè)計(jì)(1耀态,2,3暂雹,4步)首装,自底向下實(shí)現(xiàn)(5),先測(cè)量杭跪,后優(yōu)化(6)仙逻。
什么樣的架構(gòu)師是好架構(gòu)師?
- 每天都在學(xué)習(xí)涧尿,新技術(shù)新思想上手速度快系奉,理解速度快。
做不到這點(diǎn)姑廉,你就是碼農(nóng)缺亮。
- 業(yè)務(wù)出身,或者至少非常熟悉公司所處行業(yè)或者本公司的業(yè)務(wù)庄蹋。
做不到這點(diǎn)瞬内,你就是運(yùn)維。
- 熟悉軟件工程的各種規(guī)范限书,踩過(guò)無(wú)數(shù)坑虫蝶。不會(huì)為了完成需求不擇手段,不推崇quick & dirty倦西。
做不到這點(diǎn)能真,你比較適合去競(jìng)爭(zhēng)對(duì)手那兒當(dāng)工程師。
- 及時(shí)承認(rèn)錯(cuò)誤,不要覺(jué)得承認(rèn)錯(cuò)誤會(huì)有損你架構(gòu)師的身份粉铐。
做不到這點(diǎn)疼约,公關(guān)行業(yè)比較適合你。
- 不為了炫技而炫技
做不到這點(diǎn)蝙泼,你就是高中編程愛(ài)好者程剥。
- 精益求精
什么樣的架構(gòu)叫好架構(gòu)?
- 代碼整齊汤踏,分類明確织鲸,沒(méi)有common,沒(méi)有core
- 不用文檔溪胶,或很少文檔搂擦,就能讓業(yè)務(wù)方上手
- 思路和方法要統(tǒng)一,盡量不要多元
- 沒(méi)有橫向依賴哗脖,萬(wàn)不得已不出現(xiàn)跨層訪問(wèn)
- 對(duì)業(yè)務(wù)方該限制的地方有限制瀑踢,該靈活的地方要給業(yè)務(wù)方創(chuàng)造靈活實(shí)現(xiàn)的條件
- 易測(cè)試,易拓展
- 保持一定量的超前性
- 接口少才避,接口參數(shù)少
- 高性能
代碼整齊橱夭,分類明確,沒(méi)有common工扎,沒(méi)有core
破窗理論提醒我們徘钥,如果代碼不整齊分類不明確,整個(gè)架構(gòu)會(huì)隨著一次一次的拓展而越來(lái)越混亂肢娘。
破窗理論:一個(gè)房子如果窗戶破了呈础,沒(méi)有人去修補(bǔ),隔不久橱健,其它的窗戶也會(huì)莫名其妙地被人打破而钞;一面墻,如果出現(xiàn)一些涂鴉沒(méi)有被清洗掉拘荡,很快的臼节,墻上就布滿了亂七八糟、不堪入目的東西珊皿;一個(gè)很干凈的地方网缝,人們不好意思丟垃圾,但是一旦地上有垃圾出現(xiàn)之后蟋定,人就會(huì)毫不猶豫地拋粉臊,絲毫不覺(jué)羞愧。
分類明確就是:不要讓一個(gè)類或者一個(gè)模塊做兩種不同的事情驶兜。如果有類或某模塊做了兩種不同的事情扼仲,一方面不適合未來(lái)拓展远寸,另一方面也會(huì)造成分類困難。
不要搞Common屠凶,Core這些東西驰后,開(kāi)了之后后來(lái)者一定會(huì)把這個(gè)地方搞得一團(tuán)糟,最終變成Common也不Common矗愧,Core也不Core灶芝。
不用文檔,或很少文檔唉韭,就能讓業(yè)務(wù)方上手
誰(shuí)特么會(huì)去看文檔啊监署,業(yè)務(wù)方他們已經(jīng)被產(chǎn)品經(jīng)理逼得很忙了。所以你要盡可能讓你的API名字可讀性強(qiáng)纽哥,對(duì)于iOS來(lái)說(shuō),objc這門(mén)語(yǔ)言的特性把這個(gè)做到了極致栖秕,函數(shù)名長(zhǎng)就長(zhǎng)一點(diǎn)春塌,不要緊。
思路和方法要統(tǒng)一簇捍,盡量不要多元
解決一個(gè)問(wèn)題會(huì)有很多種方案只壳,但是一旦確定了一種方案,就不要在另一個(gè)地方采用別的方案了暑塑。也就是做架構(gòu)的時(shí)候吼句,你得時(shí)刻記住當(dāng)初你決定要處理這樣類型的問(wèn)題的方案是什么,以及你的初衷是什么事格,不要搖擺不定惕艳。
另外,你當(dāng)初設(shè)立這個(gè)模塊一定是有想法有原因的驹愚,要記錄下你的解決思路远搪,不要到時(shí)候換個(gè)地方你又靈光一現(xiàn)啥的,引入了其他方案逢捺,從而導(dǎo)致異構(gòu)谁鳍。
要是一個(gè)框架里面解決同一種類似的問(wèn)題有各種五花八門(mén)的方法或者類,我覺(jué)得做這個(gè)架構(gòu)的架構(gòu)師一定是自己都沒(méi)想清楚就開(kāi)始搞了劫瞳。
沒(méi)有橫向依賴倘潜,萬(wàn)不得已不出現(xiàn)跨層訪問(wèn)
沒(méi)有橫向依賴是很重要的,這決定了你將來(lái)要對(duì)這個(gè)架構(gòu)做修補(bǔ)所需要的成本有多大志于。要做到?jīng)]有橫向依賴涮因,這是很考驗(yàn)架構(gòu)師的模塊分類能力和是否熟悉業(yè)務(wù)的。
跨層訪問(wèn)是指數(shù)據(jù)流向了跟自己沒(méi)有對(duì)接關(guān)系的模塊恨憎。有的時(shí)候跨層訪問(wèn)是不可避免的蕊退,比如網(wǎng)絡(luò)底層里面信號(hào)從2G變成了3G變成了4G郊楣,這是有可能需要跨層通知到View的。但這種情況不多瓤荔,一旦出現(xiàn)就要想盡一切辦法在本層搞定或者交給上層或者下層搞定净蚤,盡量不要出現(xiàn)跨層的情況∈湎酰跨層訪問(wèn)同樣也會(huì)增加耦合度今瀑,當(dāng)某一層需要整體替換的時(shí)候,牽涉面就會(huì)很大点把。
對(duì)業(yè)務(wù)方該限制的地方有限制橘荠,該靈活的地方要給業(yè)務(wù)方創(chuàng)造靈活實(shí)現(xiàn)的條件
把這點(diǎn)做好,很依賴于架構(gòu)師的經(jīng)驗(yàn)郎逃。架構(gòu)師必須要有能力區(qū)分哪些情況需要限制靈活性哥童,哪些情況需要?jiǎng)?chuàng)造靈活性。比如對(duì)于Core Data技術(shù)棧來(lái)說(shuō)褒翰,ManagedObject理論上是可以出現(xiàn)在任何地方的贮懈,那就意味著任何地方都可以修改ManagedObject,這就導(dǎo)致ManagedObjectContext在同步修改的時(shí)候把各種不同來(lái)源的修改同步進(jìn)去优训。這時(shí)候就需要限制靈活性朵你,只對(duì)外公開(kāi)一個(gè)修改接口,不暴露任何ManagedObject在外面揣非。
如果是設(shè)計(jì)一個(gè)ABTest相關(guān)的API的時(shí)候抡医,我們又希望增加它的靈活性。使得業(yè)務(wù)方不光可以通過(guò)Target-Action的模式實(shí)現(xiàn)ABtest早敬,也要可以通過(guò)Block的方式實(shí)現(xiàn)ABTest忌傻,要盡可能滿足靈活性,減少業(yè)務(wù)方的使用成本搁嗓。
易測(cè)試易拓展
老生常談芯勘,要實(shí)現(xiàn)易測(cè)試易拓展,那就要提高模塊化程度腺逛,盡可能減少依賴關(guān)系荷愕,便于mock。另外棍矛,如果是高度模塊化的架構(gòu)安疗,拓展起來(lái)將會(huì)是一件非常容易的事情。
保持一定量的超前性
這一點(diǎn)能看出架構(gòu)師是否關(guān)注行業(yè)動(dòng)態(tài)够委,是否能準(zhǔn)確把握技術(shù)走向荐类。保持適度的技術(shù)上的超前性,能夠使得你的架構(gòu)更新變得相對(duì)輕松茁帽。
另外玉罐,這里的超前性也不光是技術(shù)上的屈嗤,還有產(chǎn)品上的。誰(shuí)說(shuō)架構(gòu)師就不需要跟產(chǎn)品經(jīng)理打交道了吊输,沒(méi)事多跟產(chǎn)品經(jīng)理聊聊天饶号,聽(tīng)聽(tīng)他對(duì)產(chǎn)品未來(lái)走向的暢想,你就可以在合理的地方為他的暢想留一條路子季蚂。同時(shí)茫船,在創(chuàng)業(yè)公司的環(huán)境下,很多產(chǎn)品需求其實(shí)只是為了趕產(chǎn)品進(jìn)度而產(chǎn)生的妥協(xié)方案扭屁,最后還是會(huì)轉(zhuǎn)到正軌的算谈。這時(shí)候業(yè)務(wù)方可以不實(shí)現(xiàn)轉(zhuǎn)到正規(guī)的方案,但是架構(gòu)這邊料滥,是一定要為這種可預(yù)知的改變做準(zhǔn)備的然眼。
接口少,接口參數(shù)少
越少的接口越少的參數(shù)葵腹,就能越降低業(yè)務(wù)方的使用成本罪治。當(dāng)然,充要條件還是要滿足的礁蔗,如何在滿足充要條件的情況下盡可能地減少接口和參數(shù)數(shù)量,這就能看出架構(gòu)師的功力有多深厚了雁社。
高性能
高性能非常重要浴井,但是在客戶端架構(gòu)中,它不是第一考慮因素霉撵。原因有下:
客戶端業(yè)務(wù)變化非常之快磺浙,做架構(gòu)時(shí)首要考慮因素應(yīng)當(dāng)是便于業(yè)務(wù)方快速滿足產(chǎn)品需求,因此需要盡可能提供簡(jiǎn)單易用效果好的接口給業(yè)務(wù)方徒坡,而不是提供高性能的接口給業(yè)務(wù)方撕氧。
蘋(píng)果平臺(tái)的性能非常之棒,正常情況下很少會(huì)出現(xiàn)由于性能不夠?qū)е碌挠脩趔w驗(yàn)問(wèn)題喇完。
蘋(píng)果平臺(tái)的優(yōu)化手段相對(duì)有限伦泥,甚至于有些時(shí)候即便動(dòng)用了無(wú)所不用其極的手段乃至不擇手段犧牲了穩(wěn)定性,性能提高很有可能也只不過(guò)是100ms到90ms的差距锦溪。10%的性能提升對(duì)于服務(wù)端來(lái)說(shuō)很不錯(cuò)了不脯,因?yàn)榉?wù)端動(dòng)不動(dòng)就是幾十萬(wàn)上百萬(wàn)的訪問(wèn)量,幾十萬(wàn)上百萬(wàn)個(gè)10ms是很可觀的刻诊。但是對(duì)于客戶端的用戶來(lái)說(shuō)防楷,他無(wú)法感知這10ms的差別,如果從10s優(yōu)化成9s用戶還是有一定感知的则涯,但是100ms變90ms复局,我覺(jué)得吧冲簿,還是別折騰了。
其實(shí)分層這種東西亿昏,真沒(méi)啥技術(shù)含量峦剔,全憑架構(gòu)師的經(jīng)驗(yàn)和素質(zhì)
我們常見(jiàn)的分層架構(gòu),有三層架構(gòu)的:展現(xiàn)層龙优、業(yè)務(wù)層羊异、數(shù)據(jù)層。也有四層架構(gòu)的:展現(xiàn)層彤断、業(yè)務(wù)層野舶、網(wǎng)絡(luò)層、本地?cái)?shù)據(jù)層宰衙。這里說(shuō)三層平道、四層,跟TCP/IP所謂的五層或者七層不是同一種概念供炼。再具體說(shuō)就是:你這個(gè)架構(gòu)在邏輯上是幾層那就幾層一屋,具體每一層叫什么,做什么袋哼,沒(méi)有特定的規(guī)范冀墨。這主要是針對(duì)模塊分類而言的。
也有說(shuō)MVC架構(gòu)涛贯,MVVM架構(gòu)的诽嘉,這種層次劃分,主要是針對(duì)數(shù)據(jù)流動(dòng)的方向而言的弟翘。
在實(shí)際情況中虫腋,針對(duì)數(shù)據(jù)流動(dòng)方向做的設(shè)計(jì)和針對(duì)模塊分類做的設(shè)計(jì)是會(huì)放在一起的,也就是說(shuō)稀余,一個(gè)MVC架構(gòu)可以是四層:展現(xiàn)層悦冀、業(yè)務(wù)層、網(wǎng)絡(luò)層睛琳、本地?cái)?shù)據(jù)層盒蟆。
為什么流行起來(lái)的是三層架構(gòu),因?yàn)樗械哪K角色只會(huì)有三種:數(shù)據(jù)管理者师骗、數(shù)據(jù)加工者茁影、數(shù)據(jù)展示者,意思也就是丧凤,籠統(tǒng)說(shuō)來(lái)募闲,軟件只會(huì)有三層,每一層扮演一個(gè)角色愿待。其他的第四層第五層浩螺,一般都是這三層里面的其中之一分出來(lái)的靴患,最后都能歸納進(jìn)這三層的某一層中去,所以用三層架構(gòu)來(lái)描述就比較普遍要出。
那么我們?cè)趺醋龇謱樱?/h5>
應(yīng)該如何做分層鸳君,不是在做架構(gòu)的時(shí)候一開(kāi)始就考慮的問(wèn)題。雖然我們要按照自頂向下的設(shè)計(jì)方式來(lái)設(shè)計(jì)架構(gòu)患蹂,但是一般情況下不適合直接從三層開(kāi)始或颊。一般都是先確定所有要解決的問(wèn)題,先確定都有哪些模塊传于,然后再基于這些模塊再往下細(xì)化設(shè)計(jì)囱挑。然后再把這些列出來(lái)的問(wèn)題和模塊做好分類。分類之后不出意外大多數(shù)都是三層沼溜。如果發(fā)現(xiàn)某一層特別龐大平挑,那就可以再拆開(kāi)來(lái)變成四層,變成五層系草。
舉個(gè)例子:你要設(shè)計(jì)一個(gè)即時(shí)通訊的服務(wù)端架構(gòu)通熄,怎么分層?
記住找都,不要一上來(lái)就把三層架構(gòu)的規(guī)范套上去唇辨,這樣做是做不出好架構(gòu)的。
你要先確定都需要解決哪些問(wèn)題能耻。這里只是舉例子助泽,我隨意列出一點(diǎn)意思意思就好了:
- 要解決用戶登錄、退出的問(wèn)題
- 解決不同用戶間數(shù)據(jù)交流的問(wèn)題
- 解決用戶數(shù)據(jù)存儲(chǔ)的問(wèn)題
- 如果是多臺(tái)服務(wù)器的集群嚎京,就要解決用戶連接的尋址問(wèn)題
解決第一個(gè)問(wèn)題需要一個(gè)鏈接管理模塊,鏈接管理模塊一般是通過(guò)鏈接池來(lái)實(shí)現(xiàn)隐解。 解決第二個(gè)問(wèn)題需要有一個(gè)數(shù)據(jù)交換模塊鞍帝,從A接收來(lái)的數(shù)據(jù)要給到B,這個(gè)事情由這個(gè)模塊來(lái)做煞茫。 解決第三個(gè)問(wèn)題需要有個(gè)數(shù)據(jù)庫(kù)帕涌,如果是服務(wù)于大量用戶,那么就需要一個(gè)緩沖區(qū)续徽,只有當(dāng)需要存儲(chǔ)的數(shù)據(jù)達(dá)到一定量時(shí)才執(zhí)行寫(xiě)操作蚓曼。 解決第四個(gè)問(wèn)題可以有幾種解決方案,一個(gè)是集群中有那么幾臺(tái)服務(wù)器作為尋路服務(wù)器钦扭,所有尋路的服務(wù)交給那幾臺(tái)去做纫版,那么你需要開(kāi)發(fā)一個(gè)尋路服務(wù)的Daemon】颓椋或者用廣播方式尋路其弊,但如果尋路頻次非常高癞己,會(huì)造成集群內(nèi)部網(wǎng)絡(luò)負(fù)載特別大。這是你要權(quán)衡的地方梭伐,目前流行的思路是去中心化痹雅,那么要解決網(wǎng)絡(luò)負(fù)載的問(wèn)題,你就可以考慮配置一個(gè)緩存糊识。
于是我們有了這些模塊:
鏈接管理绩社、數(shù)據(jù)交換、數(shù)據(jù)庫(kù)及其配套模塊赂苗、尋路模塊
做到這里還遠(yuǎn)遠(yuǎn)沒(méi)有結(jié)束愉耙,你要繼續(xù)針對(duì)這四個(gè)模塊繼續(xù)往下細(xì)分,直到足夠小為止哑梳。但是這里只是舉例子劲阎,所以就不往下深究了。
另外鸠真,我要提醒你的是悯仙,直到這時(shí),還是跟幾層架構(gòu)毫無(wú)關(guān)系的吠卷。當(dāng)你把所有模塊都找出來(lái)之后锡垄,就要開(kāi)始整理你的這些模塊,很有可能架構(gòu)圖就是這樣:
然后這些模塊分完之后你看一下圖祭隔,嗯货岭,1、2疾渴、3千贯,一共三層,所以那就是三層架構(gòu)啦搞坝。在這里最消耗腦力最考驗(yàn)架構(gòu)師功力的地方就在于:找到所有需要的模塊, 把模塊放在該放的地方
這個(gè)例子側(cè)重點(diǎn)在于如何分層碎乃,性能優(yōu)化赂弓、數(shù)據(jù)交互規(guī)范和包協(xié)議、數(shù)據(jù)采集等其他一系列必要的東西都沒(méi)有放進(jìn)去,但看到這里摧茴,相信你應(yīng)該了解架構(gòu)師是怎么對(duì)待分層問(wèn)題的了吧赘理?
對(duì)的翘紊,答案就是沒(méi)有分層换薄。所謂的分層都是出架構(gòu)圖之后的事情了。所以你看別的架構(gòu)師在演講的時(shí)候融师,上來(lái)第一句話差不多都是:"這個(gè)架構(gòu)分為以下幾層..."右钾。但考慮分層的問(wèn)題的時(shí)機(jī)絕對(duì)不是一開(kāi)始就考慮的。另外,模塊一定要把它設(shè)計(jì)得獨(dú)立性強(qiáng)霹粥,這其實(shí)是門(mén)藝術(shù)活灭将。
另外,這雖然是服務(wù)端架構(gòu)后控,但是思路跟客戶端架構(gòu)是一樣的庙曙,側(cè)重點(diǎn)不同罷了。之所以不拿客戶端架構(gòu)舉例子浩淘,是因?yàn)檫@方面的客戶端架構(gòu)蘋(píng)果已經(jīng)幫你做好了絕大部分事情捌朴,沒(méi)剩下什么值得說(shuō)的了。
關(guān)于common文件夾的問(wèn)題张抄,僅僅是文件夾而已砂蔽,別無(wú)他意。如果后期維護(hù)出了代碼混亂可能是因?yàn)槭鸸撸头?wù)器溝通協(xié)議不統(tǒng)一左驾,或代碼review不及時(shí)。應(yīng)該有專人維護(hù)公共類极谊。
舉個(gè)例子:早年安居客的app還不是集齊所有新房诡右、二手房、租房業(yè)務(wù)的轻猖。當(dāng)你剛開(kāi)始寫(xiě)新房這個(gè)app的時(shí)候帆吻,創(chuàng)建了一個(gè)Common這個(gè)pod,這里面包含了一些對(duì)于新房來(lái)說(shuō)比較Common的代碼咙边,也包含了對(duì)于這個(gè)app來(lái)說(shuō)比較Common的代碼猜煮。過(guò)了半年或者一年,你要開(kāi)始二手房這個(gè)app败许,我覺(jué)得大多數(shù)人都會(huì)選擇讓二手房也包含這個(gè)Common王带,于是這個(gè)Common很有可能自己走上另一條發(fā)展的道路。等到了租房這個(gè)業(yè)務(wù)要開(kāi)app的時(shí)候市殷,Common已經(jīng)非常之龐大愕撰,相信這時(shí)候的你也不會(huì)去想整理Common的事情了,先把租房搞定被丧,于是Common最終就變成了一坨屎。
還有就是绪妹,Common本身就是一個(gè)粒度非常大的模塊甥桂。在阿里這樣大規(guī)模的團(tuán)隊(duì)中,即便新開(kāi)一個(gè)業(yè)務(wù)邮旷,都需要在整個(gè)app的環(huán)境下開(kāi)發(fā)黄选,為什么?因?yàn)槟K拆分粒度不夠,要想開(kāi)一個(gè)新業(yè)務(wù)办陷,必須把其他業(yè)務(wù)的代碼以及依賴全部拉下來(lái)貌夕,然后再開(kāi)新入口,你的新業(yè)務(wù)才能進(jìn)行正常的代碼編寫(xiě)和調(diào)試民镜。然而你的新業(yè)務(wù)其實(shí)只依賴首頁(yè)入口啡专、網(wǎng)絡(luò)庫(kù)等這幾個(gè)小模塊,不需要依賴其他那么多的跟你沒(méi)關(guān)系的業(yè)務(wù)≈迫Γ現(xiàn)在每次打開(kāi)天貓的項(xiàng)目们童,我都要等個(gè)兩三分鐘,這非常之蛋疼鲸鹦。
但是大家真的不知道這個(gè)原因嗎慧库?知道了這個(gè)原因,為什么沒(méi)人去把這些粒度不夠細(xì)的模塊整理好馋嗜?在我看來(lái)齐板,這件事沒(méi)人敢做。原來(lái)大家用的好好的葛菇,手段爛就爛一點(diǎn)甘磨,你改了你能保證不出錯(cuò)?
這么復(fù)雜的東西熟呛,短期之內(nèi)你肯定搞不好宽档,任務(wù)量和工時(shí)都不好估,你leader會(huì)覺(jué)得你在騙工時(shí)玩自己的事情庵朝。就算你搞定了吗冤,QA這邊肯定再需要做一次全面的回歸測(cè)試,任務(wù)量極大九府,難以說(shuō)服他們配合你的工作椎瘟。
花這么大的成本只是為了減少開(kāi)啟項(xiàng)目時(shí)候等待IDE打開(kāi)時(shí)的那幾分鐘時(shí)間?我想如果我是你leader侄旬,我也應(yīng)該不會(huì)批準(zhǔn)你做這樣的事情的肺蔚。所以,與其到了后面吃這個(gè)苦頭儡羔,不如一開(kāi)始做架構(gòu)的時(shí)候就不要設(shè)置Common宣羊,到后面就能省力很多。架構(gòu)師的工作為什么是功在當(dāng)代利在千秋汰蜘,架構(gòu)師的素質(zhì)為什么對(duì)團(tuán)隊(duì)這么重要仇冯?我覺(jué)得這里就是一個(gè)最好的體現(xiàn)。
簡(jiǎn)而言之族操,不建議開(kāi)Common的原因如下:
Common不僅僅是一個(gè)文件夾苛坚,它也會(huì)是一個(gè)Pod。不管是什么,在Common里面很容易形成錯(cuò)綜復(fù)雜的小模塊依賴泼舱,在模塊成長(zhǎng)過(guò)程中等缀,會(huì)縱容工程師不注意依賴的管理,乃至于將來(lái)如果要將模塊拆分出去娇昙,會(huì)非常的困難尺迂。
Common本身與細(xì)粒度模塊設(shè)計(jì)的思想背道而馳,屬于一種不合適的偷懶手段涯贞,在將來(lái)業(yè)務(wù)拓張會(huì)成為阻礙枪狂。
一旦設(shè)置了Common,就等于給地獄之門(mén)打開(kāi)了一個(gè)小縫宋渔,每次業(yè)務(wù)迭代都會(huì)有一些不太好分類的東西放入Common州疾,這就給維護(hù)Common的人帶來(lái)了非常大的工作量,而且這些工作量全都是體力活皇拣,非常容易出錯(cuò)严蓖。
那么,不設(shè)Common會(huì)帶來(lái)哪些好處氧急?
強(qiáng)迫工程師在業(yè)務(wù)拓張的時(shí)候?qū)⒁蕾嚬芾淼氖虑榭紤]進(jìn)去颗胡,讓模塊在一開(kāi)始發(fā)展的時(shí)候就有自己的土壤,成長(zhǎng)空間和靈活度非常大吩坝。
減少各業(yè)務(wù)模塊或者Demo的體積毒姨,不需要的模塊不會(huì)由于Common的存在而包含在內(nèi)。
可維護(hù)性大大提高钉寝,模塊升級(jí)之后要做的同步工作非常輕松弧呐,解放了那個(gè)苦逼的Common維護(hù)者,更多的時(shí)間可以用在更實(shí)質(zhì)的開(kāi)發(fā)工作上嵌纲。
符合細(xì)粒度模塊劃分的架構(gòu)思想俘枫。
Common的好處只有一個(gè),就是前期特別省事兒逮走。然而它的壞處比好處要多太多鸠蚪。不設(shè)置Common,再小的模塊再小的代碼也單獨(dú)拎出來(lái)师溅,最多就是Podfile里面要多寫(xiě)幾行茅信,多寫(xiě)幾行最多只花費(fèi)幾分鐘。但若要消除Common所帶來(lái)的罪孽墓臭,不是這幾分鐘就能搞定的事情蘸鲸。既然不用Common的好處這么多,那何樂(lè)而不為呢起便?
假設(shè)將來(lái)你的項(xiàng)目中有一個(gè)類是用來(lái)做Location的棚贾,哪怕只有兩個(gè)文件,也給他開(kāi)一個(gè)模塊就叫Location榆综。如果你的項(xiàng)目中有一個(gè)類是用來(lái)做ImageProcess的妙痹,那也開(kāi)一個(gè)模塊就叫ImageProcess。不要都放到Common里面去鼻疮,將來(lái)你再開(kāi)新的項(xiàng)目或者新的業(yè)務(wù)怯伊,用Location就寫(xiě)依賴Location,用ImageProcess就寫(xiě)依賴ImageProcess判沟,不要再依賴Common了耿芹,這樣你的項(xiàng)目也好管理,管理Common的那個(gè)人日子過(guò)得也輕松挪哄,將來(lái)要升級(jí)吧秕,顧慮也少。