引言:一個app的初始階段,必然是先滿足各種業務需求。然後,經過多次版本迭代之後,先前的由於急於滿足需求而導致的雜亂程式碼則會充斥整個專案。而此時,專案有了一定的規模,有了一定數量的開發人員,那麼為了達到快速迭代版本的需求,則是需要有一個強大的架構來支撐。
在開始談app架構之前,曾經我一度認為,一個好的app就是需要有好的架構,如果沒有一個我所認為的“好架構”,那麼這個app就是很low。
直到去年參加北京ArchSummit時,聽了無數的公司他們對於產品的架構之後,我陷入沉思,因為我總在自己的認知裡選出一個自己認為最好的架構,然後覺得其他架構都是垃圾。
靜下心來想想,每個產品都有自己不同的定位,如果拋開它們的定位,拋開它們的業務需求去談如果給它們設計一個良好的架構,這簡直是扯淡。
更何況很多優秀的app架構也是由一開始很弱而慢慢變得越來越強。
所以沒有最好的架構,只有適合自己的業務的架構才是最好的架構,並且它是逐步地變強變大。
本文將舉一個例子來演示這個過程。
那麼,到底什麼是架構?
架構,又名軟體架構,是有關軟體整體結構與元件的抽象描述,用於指導大型軟體系統各個方面的設計。
以我的理解,它就像是人的骨架一般,一個人從小生長到大,圍繞著整個骨架去發展,變高變胖。
可以把app開發看成一個汽車工廠的流水線,造車身->噴漆->組裝等等。即把整個開發流程切成一個個模組,每個模組相互獨立,併發工作。這就是所謂app架構。
沒有架構的“架構”
某天,一個叫Jim的開發者,他打算開發一個app,當然有一定計算機基礎的他知道採用MVC的設計模式來構造app,於是一個星期後,終於能跑起來一個app,但是此時,看一下專案的目錄結構:
嗯,不錯,我們不但能run,還能看出這個app用了MVC的設計模式耶。
但是隨著開發的頁面越來越多,一個月後,app有了10個頁面,此時,開啟Controller、View、Model這三個資料夾之後,發現每個資料夾裡面竟然有幾十個檔案,它們雜亂無章的灑落在一起。此時不斷有使用者向Jim反映,xxx地方怎麼按鈕位置不對,xxx位置網路請求不成功。
頭痛的Jim才知道,當初應該把粒度分的更細,於是又了接下來的架構。
分模組的架構
Jim把不同的功能模組放在一塊兒,於是得到了如下的架構:
但是不對,一些工具類,公用類該放哪呢?Jim仔細思索了一番,於是又將上述架構進行改進,得到以下的架構:
嗯,這看起來才像樣嘛。
Cocoapods
慢慢地,Jim發現網上有很多可以現成拿來用的第三方框架,而他同時也學習了Cocoapods這個神器。
什麼是Cocoapods:
CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. It has over eighteen thousand libraries and can help you scale your projects elegantly.
它是一個能讓你方便地管理第三方庫的一個工具。
於是,專案變成了這樣:
此時的架構已經滿足了個人開發者,或者說是小型開發團隊的需求了。
多人開發的架構
但是在一個初具規模的公司,上述的架構模式是遠遠不行的,試想一下,如果有20個人同時開發一個app,而此時大家就算各自負責自己的模組,如果同時有不同模組的同學新增或者刪除檔案,對於.xcodeproj
檔案來說,會有嚴重的衝突。
那麼,怎麼辦呢?
想一下cocoapods的作用吧,其實利用它能做很多很多事,我們完全可以把Home、Detail、Login
等模組抽出來,也把它視為“第三方庫”(實際上可以算是二方庫)。初期階段可以這麼做:
可以看到我們把Home
這個模組抽出來了。
這麼做有什麼好處?
如果我們把各個業務模組都抽出來,首先來說,可以解決衝突的問題,並且業務團隊之間的工作不受影響,並且可以並行開發,也無需再等待其他兄弟業務團隊的進度了。
當其他業務團隊的任務完成時,我們只需pod update
,將程式碼升級到最新就可以了。
並且當一個公司有多個app時,如果有公用的模組,用這種方式會更優雅。
當然,如果你覺得直接指定git地址太low,你完全可以搞一個私有Spec,專門存放業務模組程式碼。
子project模式的架構
很多人會說上述方式很複雜,還不如採用在主工程下中放子工程(業務模組)來實現抽象:
但是我覺得這個方式管理起專案很麻煩,更新模組程式碼、不同app間複用模組程式碼都會沒上述的方式優雅,所以我覺得這種方式沒有上述的好。
對各個模組進行解耦
現在雖然我們對各個模組進行了抽象,但是業務模組間還是互相引用,對於開發來說,雖然解決了程式碼的耦合問題,對於程式碼的引用關係卻沒有改變:
上圖才只有4個模組,如果有上百個模組的話,這個關係可以複雜成一個龐大的蜘蛛網,這對於後期維護來說,成本是巨大的。
所以我們想,有木有好的方式來解耦呢,當然是有的,我覺得以下兩個方式是很好的:
- middleman
- urlRoute
先來介紹middleman(中間人模式)
如圖所示,我們只需將所有的模組依賴這個middleman,讓middleman來處理各個模組的關係,模組A如果需要依賴模組B,完全可以考middleman來處理,並且返回模組A所需要的模組B的內容,這樣就解決了解耦。
再來說說urlRoute,其實這個方式類似於Facebook很早前的320結構了。
思路就是將每個頁面看成是一個url,設定一套路由規則,在頁面開啟時將url進行路由查詢,然後這樣一來,只要模組A按照既定的規則寫好url,那麼模組B依賴模組A時,只需根據url開啟就行了。
middleman VS urlRoute
兩種方式各自有自己的優點和缺點,他們主要的區別在於:
- 傳參的方式
- 所需維護的具體內容不同
我更傾向於urlRoute,為什麼這麼說呢?
由於本文講的是app架構,這裡就簡單介紹下:
如果一個頁面出現了問題,我們可以用各種patch的方式來打補丁。但是,如果一個頁面出現了致命的錯誤,打patch的成本過於高的話,此時如果用urlRoute,則有個妙招。
我們可以更改appConfig(app的配置,可以從服務端動態更新),更改頁面所對應的url,並且改成在路由規則裡跳到webview的規則。
這樣之後,當跳到此模組時,則會開啟對應的H5頁面,而不是原來的有問題的頁面。
當然middleman也可以處理成這樣,但是從實現的角度來說,顯然是urlRoute更為優秀。
總結
當然,架構遠遠不是一篇文章能講清楚的,也不僅僅只是結構層次方面的內容,還有例如push、hotpatch、動態化、appConf、Service中介軟體模組的具體實現,MVC\MVVM\MVCS設計模式等等。
本文只是從最基礎的地方展現具體業務模組的解耦方式,希望能起到拋磚引玉的作用。
個人微博:@kuailejim
個人部落格:kuailejim.com