App架構(gòu)設(shè)計

App架構(gòu)設(shè)計經(jīng)驗談:技術(shù)選型

App架構(gòu)設(shè)計經(jīng)驗談:接口的設(shè)計

App架構(gòu)設(shè)計經(jīng)驗談:數(shù)據(jù)層的設(shè)計

App架構(gòu)設(shè)計經(jīng)驗談:業(yè)務(wù)層的設(shè)計

App架構(gòu)設(shè)計經(jīng)驗談:展示層的設(shè)計

展示層

展示層是三層架構(gòu)中最復(fù)雜的一層了,需要考慮的包括但不限于界面布局、屏幕適配、文字大小、顏色、圖片資源、提示信息、動畫等等。展示層也是變化最頻繁的一個層面,每天改得最多的就是界面了。因此,展示層也是最容易變得混亂不堪的一個層面。一個良好的展示層,應(yīng)該有較好的可讀性、健壯性、維護(hù)性、擴(kuò)展性。

三原則

我在Android項目重構(gòu)之路:界面篇中提到過三個原則,要設(shè)計好展示層,至少需要遵循好這三條基本的原則:

保持規(guī)范性:定義好開發(fā)規(guī)范,包括書寫規(guī)范、命名規(guī)范、注釋規(guī)范等,并按照規(guī)范嚴(yán)格執(zhí)行;

保持單一性:布局就只做布局,內(nèi)容就只做內(nèi)容,各自分離好,每個方法、每個類,也只做一件事情;

保持簡潔性:保持代碼和結(jié)構(gòu)的簡潔,每個方法,每個類,每個包,每個文件,都不要塞太多代碼或資源,感覺多了就應(yīng)該拆分。

關(guān)于這三個原則詳細(xì)的解說,界面篇已經(jīng)講過的,我這里就不再重復(fù)。在此,我只做些補(bǔ)充。

關(guān)于規(guī)范,Android方面,我已經(jīng)分享過一套Android技術(shù)積累:開發(fā)規(guī)范,主要分為書寫規(guī)范、命名規(guī)范、注釋規(guī)范三部分。iOS方面,蘋果已經(jīng)有一套Coding Guidelines,主要屬于命名方面的規(guī)范。當(dāng)我們制定自己的開發(fā)規(guī)范時,首先就要遵守蘋果的這份規(guī)范,在此基礎(chǔ)上再加上自己的規(guī)范。

最重要的不是開發(fā)規(guī)范的制定,而是開發(fā)規(guī)范的執(zhí)行。如果沒有按照開發(fā)規(guī)范去執(zhí)行,那開發(fā)規(guī)范就等于形同虛設(shè),那代碼混亂的問題依然得不到解決。

另外,Android系統(tǒng)本身已經(jīng)對資源進(jìn)行了很好的分離,字符串、顏色值、尺寸大小、圖片、動畫等等都用不同的xml文件定義。而iOS系統(tǒng)在這方面就遜色很多,只能自己實現(xiàn),其中一種實現(xiàn)方案就是通過plist文件的方式實現(xiàn)和Android一樣的機(jī)制。

工程結(jié)構(gòu)

工程結(jié)構(gòu)其實就是模塊的劃分,無非分為兩類:按業(yè)務(wù)劃分或按組件劃分。

比如一個電商App,可能會有首頁、附近、分類、我的四大模塊,工程結(jié)構(gòu)也根據(jù)這四大模塊進(jìn)行劃分,Android可能就分為了四個模塊包:

com.domain.home 首頁

com.domain.nearby 附近

com.domain.category 分類

com.domain.user 我的

同樣的,iOS則分為四個分組:home、nearby、category、user。

之后,每個模塊下相應(yīng)的頁面就放入相應(yīng)的模塊。那么,問題來了,商品詳情頁應(yīng)該屬于哪個模塊呢?首頁會跳轉(zhuǎn)到商品詳情頁,附近也會跳轉(zhuǎn)到商品詳情頁,分類也會跳轉(zhuǎn)到商品詳情頁,用戶查看訂單時也能跳轉(zhuǎn)到商品詳情頁。有些頁面,并不能很明顯的區(qū)分出屬于哪個模塊的。我接手過的,按業(yè)務(wù)劃分的二手項目中(即不是由我搭建的項目),我要找一個頁面時,我認(rèn)為應(yīng)該屬于A模塊的,但在A模塊卻找不到,問了同事才知道在B模塊。類似的情況出現(xiàn)過很多次,而且不止出現(xiàn)在我身上,對業(yè)務(wù)不熟悉的開發(fā)人員都會出現(xiàn)這個問題。而且,對業(yè)務(wù)不熟悉的開發(fā)人員開發(fā)新的頁面或功能時,如果對業(yè)務(wù)理解不深,劃分出錯,那也將成為問題,其他人員要找該頁面時更難找到了。

因此,我更喜歡按組件劃分的工程結(jié)構(gòu),因為組件每個人都懂,不管對業(yè)務(wù)熟不熟悉,查找起來都明顯方便很多。Android按組件劃分大致如下:

com.domain.activities 存放所有的Activity

com.domain.fragments 存放所有的Fragment

com.domain.adapters 存放所有的Adapter

com.domain.services 存放所有的Service

com.domain.views 存放所有的自定義View

com.domain.utils 存放所有的工具類

iOS的分組則大致如下:

controllers 存放所有ViewController

cells 存放所有Cell,包括TableViewCell和CollectionViewCell

views 存放所有自定義控件或?qū)ο到y(tǒng)控件的擴(kuò)展

utils 存放所有的工具類

基類的定義

Android的Activity、Fragment、Adapter,iOS的ViewController,分別定義一個基類,將大部分通用的變量和方法定義和封裝好,將減少很多工作量,而且有了統(tǒng)一的設(shè)置,也會減少代碼的混亂。比如我在Android項目重構(gòu)之路:實現(xiàn)篇中提到的KBaseActivity和KBaseAdapter的實現(xiàn)就是例子,當(dāng)然還可以抽離出更多變量和方法。

每個Activity的onCreate()方法,一般分為三步:

變量的初始化;

View的初始化;

加載數(shù)據(jù)。

因此,其實可以將onCreate()方法拆分成三個方法:

initVariables()

initViews()

loadData()

在基類中將這三個方法定義為抽象方法,由子類去實現(xiàn),這樣,子類就不需要實現(xiàn)onCreate()方法了,只要實現(xiàn)更細(xì)化的上述三個方法即可。

iOS的ViewController也是同樣的方式,這里就不重復(fù)了。

寫在最后

自此,該系列的文章暫時就完結(jié)了,方法論比較多,很少涉及到具體的實現(xiàn)。因為具體實現(xiàn)的方案很多,而且還要結(jié)合實際項目,無法說哪個方案好哪個方案差。但方法論大部分是想通的,所以,本系列主要講方法論。

業(yè)務(wù)層:

業(yè)務(wù)層其實并不復(fù)雜,但是大部分開發(fā)人員對其職責(zé)并沒有理解清楚,從而使其淪落為一個數(shù)據(jù)中轉(zhuǎn)站。我之前分享過的Android項目重構(gòu)之路系列中提到的核心層,其實就是這里所講的業(yè)務(wù)層。但有不少讀者反映,他們在實際項目中就只是做一下參數(shù)檢查,然后直接調(diào)用API,與展示層對接的接口基本也與API的接口一致的。這樣,業(yè)務(wù)層無疑就已經(jīng)變?yōu)榱艘粋€數(shù)據(jù)中轉(zhuǎn)站。

業(yè)務(wù)層的職責(zé)

所以,設(shè)計業(yè)務(wù)層之前,對業(yè)務(wù)層的職責(zé)要先真正理解清楚。這里,我舉兩個栗子說明一下。

第一個是新用戶注冊的例子。注冊時,界面上一般都會要求用戶輸入手機(jī)號、驗證碼、密碼和確認(rèn)密碼。但是,API接口一般只會有三個參數(shù):手機(jī)號、驗證碼和密碼,不會有確認(rèn)密碼。因此,調(diào)用接口之前,密碼和確認(rèn)密碼的一致性檢查是必須的。同時,也要檢查這些數(shù)據(jù)是否為空、手機(jī)號是否符合規(guī)范、驗證碼是否有效、密碼有沒有包含了特殊字符等。正確姿勢就是當(dāng)所有檢查都通過了之后,才調(diào)用API接口。最后,調(diào)用注冊接口成功后,可能還要再調(diào)用一次登錄接口,并可能將用戶登錄信息緩存起來,方便用戶下次啟動應(yīng)用時自動登錄。所有這些都屬于業(yè)務(wù)邏輯處理,也就是業(yè)務(wù)層的工作。

第二個是涉及用戶驗證的例子。比如,在一個電商App,當(dāng)用戶瀏覽某個商品,點擊購買時,App首先會判斷用戶是否已經(jīng)登錄,如未登錄,則會跳轉(zhuǎn)到登錄頁面讓用戶先登錄。如果已經(jīng)登錄,但token已經(jīng)過期,那需要先去獲取新的token,之后才能進(jìn)行下一步的購物操作。這些邏輯處理,也是業(yè)務(wù)層的工作。

因此,簡單點說,業(yè)務(wù)層就是處理業(yè)務(wù)邏輯,包括數(shù)據(jù)的檢查、業(yè)務(wù)分支的處理等。比如上面第二個例子,可能很多人就會將用戶是否已經(jīng)登錄的判斷直接在界面上做處理,當(dāng)確認(rèn)登錄后,token也是有效的之后,才調(diào)用業(yè)務(wù)層做購買商品的操作,這就是導(dǎo)致業(yè)務(wù)層淪落為API的數(shù)據(jù)中轉(zhuǎn)站的直接表現(xiàn)。

業(yè)務(wù)層的交互

只有真正理解了業(yè)務(wù)層的職責(zé)之后,才能有效地設(shè)計業(yè)務(wù)層與外層的交互接口。

業(yè)務(wù)層向下,與數(shù)據(jù)層交互;向上,與展示層交互。

與數(shù)據(jù)層交互只是調(diào)用數(shù)據(jù)層的接口獲取數(shù)據(jù),而與展示層交互則需要提供接口給展示層調(diào)用。因為業(yè)務(wù)處理一般屬于比較耗時的操作,主要在于底層的網(wǎng)絡(luò)請求比較耗時,所以提供給展示層的接口數(shù)據(jù)結(jié)果應(yīng)該以異步的方式提供,因此,接口上就需要提供個回調(diào)參數(shù),返回業(yè)務(wù)處理之后的結(jié)果。我之前分享過的Android項目重構(gòu)之路:實現(xiàn)篇有講到一種實現(xiàn)方式,可參考。

寫在最后

業(yè)務(wù)層可以說是一個數(shù)據(jù)加工場,處理核心的業(yè)務(wù)邏輯。其實,只要理解清楚了業(yè)務(wù)層的職責(zé),業(yè)務(wù)層就不難實現(xiàn)。

數(shù)據(jù)層,是三層架構(gòu)中的最底層,負(fù)責(zé)數(shù)據(jù)的管理。它主要的任務(wù)就是:

調(diào)用網(wǎng)絡(luò)API,獲取數(shù)據(jù);

將數(shù)據(jù)緩存到本地;

將數(shù)據(jù)交付給上一層。

根據(jù)這三個任務(wù),數(shù)據(jù)層可以再拆分為三層:網(wǎng)絡(luò)層、本地數(shù)據(jù)層、交付層。

網(wǎng)絡(luò)層

還有一些在前面的文章中沒有提及到的,在此做一些補(bǔ)充。

首先是不同網(wǎng)絡(luò)狀態(tài)的處理。當(dāng)網(wǎng)絡(luò)不可用時,則不應(yīng)該再去調(diào)用API;當(dāng)網(wǎng)絡(luò)可用,但不是WIFI時,有些比較耗流量的操作也應(yīng)該禁止,比如上傳和下載大文件;當(dāng)網(wǎng)絡(luò)狀態(tài)不同時,還可以采用不同的網(wǎng)絡(luò)策略,比如,當(dāng)網(wǎng)絡(luò)為WIFI時,當(dāng)前API可以返回更多更全面的數(shù)據(jù),還可以預(yù)先加載相關(guān)聯(lián)的其他API。

其次,為了節(jié)省流量,接口的設(shè)計上可以對數(shù)據(jù)進(jìn)行簡化。例如,對于一些列表類的接口,可以這么設(shè)計:只返回更新的部分,比如,上一次請求返回了10條按時間排序的數(shù)據(jù),第一條數(shù)據(jù)為最新的,id為101,當(dāng)發(fā)起下一次請求時,將101的id作為參數(shù)調(diào)用API,API查到該id,發(fā)現(xiàn)該id之后又新增了兩條數(shù)據(jù),API則只返回新增的這兩條數(shù)據(jù)。

另外,為了保證程序的健壯性,調(diào)用API時,對入?yún)⒌暮戏ㄐ詸z查也是很有必要的。而且,也應(yīng)該定義好本地的錯誤碼和錯誤信息,保證每個錯誤都能正常解析。

本地數(shù)據(jù)層

本地數(shù)據(jù)層主要就是做緩存處理,這需要設(shè)計好一套緩存策略。設(shè)計緩存策略時,有幾個問題需要考慮清楚:

哪些需要緩存?哪些不需要緩存?

緩存在哪里?數(shù)據(jù)庫?文件?還是內(nèi)存?

緩存時間多長?

哪些需要緩存?

將所有數(shù)據(jù)都緩存是不明智的,不同的數(shù)據(jù)應(yīng)該有不同的緩存策略,比如一個電商App,首頁的商品列表數(shù)據(jù)應(yīng)該緩存,而且緩存時間應(yīng)該比較長,而每個商品的詳情數(shù)據(jù)就沒必要緩存或緩存時間很短。對于一份數(shù)據(jù)需不需要緩存,判斷標(biāo)準(zhǔn)可以是:用戶查看該數(shù)據(jù)的頻率高不高?首頁商品列表是用戶每次啟動都會看到的,而每個商品的詳情用戶最多只看幾次。

緩存在哪里?

從內(nèi)存讀取數(shù)據(jù)是最快的,但內(nèi)存非常有限。因此,內(nèi)存一般只用來緩存使用頻率非常高的數(shù)據(jù)。

文件緩存主要就是圖片、音頻、視頻了。

數(shù)據(jù)庫可以保存大量數(shù)據(jù),主要就是用于保存商品列表、聊天記錄之類的關(guān)系型數(shù)據(jù)。

然而,不管緩存在哪里,都需要限定好緩存的容量,要定期清理,不然會越積越多。

緩存時間多長?

首先,每份緩存數(shù)據(jù)都應(yīng)該設(shè)置一個緩存的有效時間,有效期的起始時間以最后一次被調(diào)用的時間為準(zhǔn),當(dāng)該數(shù)據(jù)長時間沒有再被調(diào)用到時,就應(yīng)該從緩存中清理掉。

緩存的有效時間應(yīng)該設(shè)多長呢?可以短至一分鐘,長至一星期甚至一個月,具體因數(shù)據(jù)而異。一般內(nèi)存的緩存時間不宜太長,程序退出基本就要全部清理了。文件緩存可以設(shè)置保留一天或一個星期,可以每隔一天清理一次。數(shù)據(jù)庫緩存再久一些也無所謂,但最好還是不要超過一個月。

交付層

交付層其實就是一個向上層開放的交互接口層,是上層向數(shù)據(jù)層獲取數(shù)據(jù)的入口。上層向數(shù)據(jù)層請求數(shù)據(jù),它是不關(guān)心數(shù)據(jù)層的數(shù)據(jù)是從緩存獲取還是從網(wǎng)絡(luò)獲取的,它只關(guān)心結(jié)果,數(shù)據(jù)層能給到它想要的數(shù)據(jù)結(jié)果就OK了。因此,交付層主要就是定義一堆開放的接口或協(xié)議。

如果接口或協(xié)議非常多,那么,將接口或協(xié)議按照模塊劃分也是有必要的。比如微信,按模塊劃分有:IM、公眾號、朋友圈、錢包、購物、游戲等等。模塊之間應(yīng)該盡量相對獨(dú)立、松耦合。

寫在最后

數(shù)據(jù)層如果再擴(kuò)展,還可以再加入日志管理,這里就不再展開講了。上面內(nèi)容講得也比較亂,有哪里講得不好的地方歡迎吐槽。

接口的設(shè)計

App與服務(wù)器的通信接口如何設(shè)計得好,需要考慮的地方挺多的,在此根據(jù)我的一些經(jīng)驗做一些總結(jié)分享,旨在拋磚引玉。

安全機(jī)制的設(shè)計

現(xiàn)在,大部分App的接口都采用RESTful架構(gòu),RESTFul最重要的一個設(shè)計原則就是,客戶端與服務(wù)器的交互在請求之間是無狀態(tài)的,也就是說,當(dāng)涉及到用戶狀態(tài)時,每次請求都要帶上身份驗證信息。實現(xiàn)上,大部分都采用token的認(rèn)證方式,一般流程是:

用戶用密碼登錄成功后,服務(wù)器返回token給客戶端;

客戶端將token保存在本地,發(fā)起后續(xù)的相關(guān)請求時,將token發(fā)回給服務(wù)器;

服務(wù)器檢查token的有效性,有效則返回數(shù)據(jù),若無效,分兩種情況:

token錯誤,這時需要用戶重新登錄,獲取正確的token

token過期,這時客戶端需要再發(fā)起一次認(rèn)證請求,獲取新的token

然而,此種驗證方式存在一個安全性問題:當(dāng)?shù)卿浗涌诒唤俪謺r,黑客就獲取到了用戶密碼和token,后續(xù)則可以對該用戶做任何事情了。用戶只有修改密碼才能奪回控制權(quán)。

如何優(yōu)化呢?第一種解決方案是采用HTTPS。HTTPS在HTTP的基礎(chǔ)上添加了SSL安全協(xié)議,自動對數(shù)據(jù)進(jìn)行了壓縮加密,在一定程序可以防止監(jiān)聽、防止劫持、防止重發(fā),安全性可以提高很多。不過,SSL也不是絕對安全的,也存在被劫持的可能。另外,服務(wù)器對HTTPS的配置相對有點復(fù)雜,還需要到CA申請證書,而且一般還是收費(fèi)的。而且,HTTPS效率也比較低。一般,只有安全要求比較高的系統(tǒng)才會采用HTTPS,比如銀行。而大部分對安全要求沒那么高的App還是采用HTTP的方式。

我們目前的做法是給每個接口都添加簽名。給客戶端分配一個密鑰,每次請求接口時,將密鑰和所有參數(shù)組合成源串,根據(jù)簽名算法生成簽名值,發(fā)送請求時將簽名一起發(fā)送給服務(wù)器驗證。類似的實現(xiàn)可參考OAuth1.0的簽名算法。這樣,黑客不知道密鑰,不知道簽名算法,就算攔截到登錄接口,后續(xù)請求也無法成功操作。不過,因為簽名算法比較麻煩,而且容易出錯,只適合對內(nèi)的接口。如果你們的接口屬于開放的API,則不太適合這種簽名認(rèn)證的方式了,建議還是使用OAuth2.0的認(rèn)證機(jī)制。

我們也給每個端分配一個appKey,比如Android、iOS、微信三端,每個端分別分配一個appKey和一個密鑰。沒有傳appKey的請求將報錯,傳錯了appKey的請求也將報錯。這樣,安全性方面又加多了一層防御,同時也方便對不同端做一些不同的處理策略。

另外,現(xiàn)在越來越多App取消了密碼登錄,而采用手機(jī)號+短信驗證碼的登錄方式,我在當(dāng)前的項目中也采用了這種登錄方式。這種登錄方式有幾種好處:

不需要注冊,不需要修改密碼,也不需要因為忘記密碼而重置密碼的操作了;

用戶不再需要記住密碼了,也不怕密碼泄露的問題了;

相對于密碼登錄其安全性明顯提高了。

接口數(shù)據(jù)的設(shè)計

接口的數(shù)據(jù)一般都采用JSON格式進(jìn)行傳輸,不過,需要注意的是,JSON的值只有六種數(shù)據(jù)類型:

Number:整數(shù)或浮點數(shù)

String:字符串

Boolean:true 或 false

Array:數(shù)組包含在方括號[]中

Object:對象包含在大括號{}中

Null:空類型

所以,傳輸?shù)臄?shù)據(jù)類型不能超過這六種數(shù)據(jù)類型。以前,我們曾經(jīng)試過傳輸Date類型,它會轉(zhuǎn)為類似于"2016年1月7日 09時17分42秒 GMT+08:00"這樣的字符串,這在轉(zhuǎn)換時會產(chǎn)生問題,不同的解析庫解析方式可能不同,有的可能會轉(zhuǎn)亂,有的可能直接異常了。要避免出錯,必須做特殊處理,自己手動去做解析。為了根除這種問題,最好的解決方案是用毫秒數(shù)表示日期。

另外,以前的項目中還出現(xiàn)過字符串的"true"和"false",或者字符串的數(shù)字,甚至還出現(xiàn)過字符串的"null",導(dǎo)致解析錯誤,尤其是"null",導(dǎo)致App奔潰,后來查了好久才查出來是該問題導(dǎo)致的。這都是因為服務(wù)端對數(shù)據(jù)沒處理好,導(dǎo)致有些數(shù)據(jù)轉(zhuǎn)為了字符串。所以,在客戶端,也不能完全信任服務(wù)端傳回的數(shù)據(jù)都是對的,需要對所有異常情況都做相應(yīng)處理。

服務(wù)器返回的數(shù)據(jù)結(jié)構(gòu),一般為:

{code:0,message:"success",data:{key1:value1,key2:value2,...}}

code: 返回碼,0表示成功,非0表示各種不同的錯誤

message: 描述信息,成功時為"success",錯誤時則是錯誤信息

data: 成功時返回的數(shù)據(jù),類型為對象或數(shù)組

不同錯誤需要定義不同的返回碼,屬于客戶端的錯誤和服務(wù)端的錯誤也要區(qū)分,比如1XX表示客戶端的錯誤,2XX表示服務(wù)端的錯誤。這里舉幾個例子:

0:成功

100:請求錯誤

101:缺少appKey

102:缺少簽名

103:缺少參數(shù)

200:服務(wù)器出錯

201:服務(wù)不可用

202:服務(wù)器正在重啟

錯誤信息一般有兩種用途:一是客戶端開發(fā)人員調(diào)試時看具體是什么錯誤;二是作為App錯誤提示直接展示給用戶看。主要還是作為App錯誤提示,直接展示給用戶看的。所以,大部分都是簡短的提示信息。

data字段只在請求成功時才會有數(shù)據(jù)返回的。數(shù)據(jù)類型限定為對象或數(shù)組,當(dāng)請求需要的數(shù)據(jù)為單個對象時則傳回對象,當(dāng)請求需要的數(shù)據(jù)是列表時,則為某個對象的數(shù)組。這里需要注意的就是,不要將data傳入字符串或數(shù)字,即使請求需要的數(shù)據(jù)只有一個,比如token,那返回的data應(yīng)該為:

//正確data:{token:123456}//錯誤data:123456

接口版本的設(shè)計

接口不可能一成不變,在不停迭代中,總會發(fā)生變化。接口的變化一般會有幾種:

數(shù)據(jù)的變化,比如增加了舊版本不支持的數(shù)據(jù)類型

參數(shù)的變化,比如新增了參數(shù)

接口的廢棄,不再使用該接口了

為了適應(yīng)這些變化,必須得做接口版本的設(shè)計。實現(xiàn)上,一般有兩種做法:

每個接口有各自的版本,一般為接口添加個version的參數(shù)。

整個接口系統(tǒng)有統(tǒng)一的版本,一般在URL中添加版本號,比如http://api.domain.com/v2。

大部分情況下會采用第一種方式,當(dāng)某一個接口有變動時,在這個接口上疊加版本號,并兼容舊版本。App的新版本開發(fā)傳參時則將傳入新版本的version。

如果整個接口系統(tǒng)的根基都發(fā)生變動的話,比如微博API,從OAuth1.0升級到OAuth2.0,整個API都進(jìn)行了升級。

有時候,一個接口的變動還會影響到其他接口,但做的時候不一定能發(fā)現(xiàn)。因此,最好還要有一套完善的測試機(jī)制保證每次接口變更都能測試到所有相關(guān)層面。

寫在最后

關(guān)于接口設(shè)計,暫時想到的就這么多了。各位看官看完覺得有遺漏或有哪些需要優(yōu)化的歡迎提出一起討論。

技術(shù)選型

當(dāng)你做架構(gòu)設(shè)計時,必然會面臨技術(shù)選型的抉擇,不同的技術(shù)方案,架構(gòu)也可能完全不同。有哪些技術(shù)選型需要做決策呢?比如,App是純原生開發(fā),還是Web App,抑或Hybrid App?iOS開發(fā),語言上是選擇Objective-C還是Swift?架構(gòu)模式用MVC,還是MVP,或者M(jìn)VVM?下面根據(jù)我的一些經(jīng)驗對某些方面做點總結(jié)分享。

原生/H5

關(guān)于用原生好,還是用H5好的爭論從沒間斷過。但我覺得,脫離了實際場景來討論孰好孰壞意義不大。就說我們目前正在做的項目,先說明下背景:

不止要做Android和iOS App,也要做微信公眾號;

H5人員缺乏,只有一兩個兼職的可用,而且不可控因素很高;

我們對原生比較熟;

開發(fā)時間只有半個月。

首先,需求上來說,大部分頁面用H5實現(xiàn),可以減少很多工作量。但因為不可控因素太高,而時間又短,風(fēng)險太大。而我們對原生比較熟,開發(fā)效率比較高,很多東西我也控制得了,風(fēng)險相對比較低。而且,我們的主推產(chǎn)品是App,微信屬于輔助性產(chǎn)品,所以,微信要求也沒那么高。因此,我決定以原生為主,H5為輔,App大部分頁面用原生完成,小部分用WebView加載H5。

另外,WebView加載H5也有兩種模式,一種是加載服務(wù)器的H5頁面,一種是加載本地的H5頁面。加載服務(wù)器的H5頁面比較簡單,WebView只要load一下URL就可以了。加載本地的H5頁面,則需要將H5文件存放在本地,包括關(guān)聯(lián)的CSS和JS文件。這種方式相對比較復(fù)雜,不過,加載速度會比第一種快很多。我們當(dāng)前項目基于上面考慮,只能選擇第一種方案。

Objective-C/Swift

我在項目中選擇了Swift,主要基于三個原因:

Swift真的很簡潔,生產(chǎn)效率很高;

Swift取代Objective-C是必然的趨勢;

目前iOS只有我一個人開發(fā),不需要顧慮到團(tuán)隊里沒人懂Swift。

如果你的團(tuán)隊里沒人懂Swift,那還是乖乖用Objective-C吧;如果有一兩個懂Swift的,那可以混合開發(fā),并讓不懂的人盡快學(xué)會Swift;如果都懂了,不用想了,直接上Swift吧。

當(dāng)語言上選擇了Swift,相應(yīng)的一些第三方庫也面臨著選型。比如,依賴庫管理,Objective-C時代大部分用CocoaPods,Swift時代,我更喜歡Carthage。Carhage是用Swift寫的,和CocoaPods相比,輕耦合,也更靈活。我個人也不太喜歡CocoaPods,使用起來比較麻煩,耦合性也較高,我使用過程中也經(jīng)常出問題,而且還總是不知道該怎么解決,要移除時也是非常麻煩。

再推薦幾個關(guān)于Swift的第三方庫:

MVC/MVP/MVVM

先分別簡單介紹下這三個架構(gòu)模式吧:

MVC:Model-View-Controller,經(jīng)典模式,很容易理解,主要缺點有兩個:

View對Model的依賴,會導(dǎo)致View也包含了業(yè)務(wù)邏輯;

Controller會變得很厚很復(fù)雜。

MVP:Model-View-Presenter,MVC的一個演變模式,將Controller換成了Presenter,主要為了解決上述第一個缺點,將View和Model解耦,不過第二個缺點依然沒有解決。

MVVM:Model-View-ViewModel,是對MVP的一個優(yōu)化模式,采用了雙向綁定:View的變動,自動反映在ViewModel,反之亦然。

架構(gòu)模式上,我不會推崇說哪種模式好,每種模式都各有優(yōu)點,也各有極限性。越高級的模式復(fù)雜性越高,實現(xiàn)起來也越難。最近火熱的微服務(wù)架構(gòu),比起MVC,復(fù)雜度不知增加了多少倍。

我在實際項目中思考架構(gòu)時,也不會想著要用哪種模式,我只思考現(xiàn)階段,以現(xiàn)有的人力資源和時間資源,如何才能更快更好地完成需求,適當(dāng)考慮下如何為后期擴(kuò)展或重構(gòu)做準(zhǔn)備。就說我前段時間分享的Android項目重構(gòu)之路系列中講的那個架構(gòu),確切地說,都不屬于上面三種架構(gòu)模式之一。

寫在最后

技術(shù)選型,決策關(guān)鍵不在于每種技術(shù)方案的優(yōu)劣如何,而在于你團(tuán)隊的水平、資源的多寡,要根據(jù)實際情況選擇最適合你們當(dāng)前階段的架構(gòu)方案。當(dāng)團(tuán)隊拓展了,資源也充足了,肯定也是需要再重構(gòu)的,到時再思考其他更合適更優(yōu)秀的方案。

本文轉(zhuǎn)載自:http://www.echojb.com/network-design/2017/02/23/330961.html 只做個人收藏

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容