前言
接觸 Android 開發(fā)已有一段時(shí)間带到。
剛開始學(xué)習(xí)時(shí)(大概2017年底)昧碉,每個(gè)星期都會(huì)做一個(gè)小小的項(xiàng)目,類似于商品瀏覽頁面+購物車、本地音樂播放器之類的被饿。那時(shí)候敲代碼只求把功能實(shí)現(xiàn)出來四康,邏輯什么的都寫在 activity/fragment 里,好在項(xiàng)目量級太輕狭握,不會(huì)有什么"代碼不優(yōu)美"的觀感闪金。
2018年寒假待在家的時(shí)候,因?yàn)椴幌胱屪约禾挑~所以打算開發(fā)一個(gè)讓用戶分享語錄的社區(qū)APP论颅。然后什么前期規(guī)劃都沒有毕泌,直接創(chuàng)建項(xiàng)目開始編寫第一個(gè)界面。那個(gè)時(shí)候基本是想到什么新功能就開始查資料嗅辣,然后跟著學(xué)習(xí)、實(shí)踐挠说,所有的業(yè)務(wù)代碼都放在 activity/fragment 里澡谭。隨著功能的不斷擴(kuò)展,每個(gè) activity/fragment 里的代碼也不斷膨脹损俭,加上我是個(gè)不太喜歡注釋的人蛙奖,所以代碼看起來就是亂糟糟的一大堆。不過我當(dāng)時(shí)沒有意識到這個(gè)問題杆兵,感覺能實(shí)現(xiàn)出自己想要的功能就厲害得不行了哈哈哈雁仲。寒假結(jié)束后我的項(xiàng)目也差不多搞完了,之后就放著不管了琐脏。直到大三下學(xué)期的期末需要提交一個(gè)項(xiàng)目攒砖,于是我又把這個(gè)APP的代碼翻出來,打算做一些功能優(yōu)化日裙。結(jié)果一打開代碼我就對自己產(chǎn)生了懷疑吹艇,實(shí)在不想承認(rèn)是自己寫的“悍鳎總體感覺就是代碼很繁冗受神,并且結(jié)構(gòu)不清晰(除了作者之外應(yīng)該沒啥人愿意看),還有很多本可以復(fù)用的邏輯沒有解耦格侯。反正后期的維護(hù)和優(yōu)化都變得十分困難鼻听,也不是難吧,就是要花精力去梳理代碼联四,這點(diǎn)還挺讓人煩躁的撑碴。
隨便放個(gè)當(dāng)初的代碼截圖,感受一下畫風(fēng) (〃-ー-)?
言歸正傳碎连。其實(shí)我上面用的模式都是最傳統(tǒng)的 MVC 吧灰羽。在這個(gè)模式中,Activity 不但承擔(dān)著 View 的角色,還包含了 Controller 的大部分邏輯廉嚼。在項(xiàng)目量級較輕時(shí)玫镐,這樣寫沒有問題。但是項(xiàng)目規(guī)模一旦擴(kuò)大怠噪,Activity 的代碼就會(huì)變得臃腫恐似,且耦合度太高,不利于持續(xù)的開發(fā)與維護(hù)傍念。
MVP 初體驗(yàn)
大四上學(xué)期(2018/11)開始準(zhǔn)備畢設(shè) APP矫夷。因?yàn)檫@時(shí)候已經(jīng)修完幾門關(guān)于"軟件工程生產(chǎn)周期"的課了,也實(shí)習(xí)過一段時(shí)間了(雖然是在寫 JS 和 Python)憋槐,接觸過部門的幾個(gè)項(xiàng)目双藕,所以有了前期準(zhǔn)備的意識。大概就是在編程之前先進(jìn)行背景調(diào)研阳仔、原型設(shè)計(jì)忧陪、架構(gòu)選型、模塊劃分之類的近范。因?yàn)橛辛饲败囍b嘶摊,所以在選擇項(xiàng)目的架構(gòu)模式時(shí),花了好幾天的時(shí)間進(jìn)行仔細(xì)考量评矩,最終敲定了 MVP叶堆。
關(guān)于 MVP 模式,本來打算在這篇博客里詳細(xì)總結(jié)一下斥杜。但是網(wǎng)上講解 MVP 模式的優(yōu)秀文章太多了虱颗,我覺得我既然無法青出于藍(lán),那也就沒有必要花精力寫那么多不痛不癢的內(nèi)容了果录。簡單地畫了兩張UML 圖上枕,談?wù)勎宜斫獾?MVP 模式吧。
定義
MVP 把 Activity 中的 UI 邏輯抽象成 View 接口弱恒,把業(yè)務(wù)邏輯抽象成 Presenter 接口辨萍,Model 類還是原來的 Model。這樣子Activity的工作變得簡單了返弹,只用來響應(yīng)生命周期锈玉,其他工作都丟到Presenter中去完成。從上圖可以看出义起,Presenter 是 Model 和 View 之間的橋梁拉背,為了讓結(jié)構(gòu)變得更加簡單,View 并不能直接對 Model 進(jìn)行操作默终,這也是 MVP 與 MVC 最大的不同之處椅棺。
使用步驟
從上圖中可以看出犁罩,使用MVP,需要經(jīng)歷以下步驟:
- 創(chuàng)建 IPresenter 接口两疚,把所有業(yè)務(wù)邏輯的接口都放在這里床估,并創(chuàng)建它的實(shí)現(xiàn) PresenterCompl(在這里可以方便地查看業(yè)務(wù)功能,由于接口可以有多種實(shí)現(xiàn)所以也方便寫單元測試)诱渤。
- 創(chuàng)建 IView 接口丐巫,把所有視圖邏輯的接口都放在這里,實(shí)現(xiàn)類是當(dāng)前的 Activity/Fragment勺美。
- 由 UML 圖可以看出递胧,Activity 里包含了一個(gè) IPresenter,而 PresenterCompl 里又包含了一個(gè) IView 并且依賴了 Model赡茸。Activity 里只保留對 IPresenter 的調(diào)用缎脾,其它工作全部留到PresenterCompl 中實(shí)現(xiàn)。
- Model 并不是必須有的占卧,但是一定會(huì)有 View 和 Presenter赊锚。
總之就是把 Activity 里的許多邏輯都抽離到 View 和 Presenter 接口中去,并由具體的實(shí)現(xiàn)類來完成屉栓。
優(yōu)點(diǎn)
對于初學(xué)者而言,可能無法一下子理解耸袜,沒關(guān)系我之前也是這樣友多,然而動(dòng)手寫了幾個(gè)界面邏輯后立馬就熟悉了。剛開始可能會(huì)覺得文件量驟增堤框,畢竟原來一個(gè) Activity 就能搞定的事域滥,現(xiàn)在我要寫 IView + Activity + IPresenter + PresenterCompl,好煩有沒有蜈抓?不過隨著項(xiàng)目工程的擴(kuò)展启绰,也就漸漸體會(huì)到 MVP 模式的好處了。
優(yōu)點(diǎn)如下:
- 分離了視圖邏輯和業(yè)務(wù)邏輯沟使,降低了耦合委可。
- Activity只處理生命周期的任務(wù),代碼變得更加簡潔腊嗡。
- 視圖邏輯和業(yè)務(wù)邏輯分別抽象到了View和Presenter的接口中去着倾,提高代碼的可閱讀性。
- Presenter被抽象成接口燕少,可以有多種具體的實(shí)現(xiàn)卡者,所以方便進(jìn)行單元測試。
- 把業(yè)務(wù)邏輯抽到 Presenter 中去客们,避免后臺(tái)線程引用著 Activity 導(dǎo)致 Activity 的資源無法被系統(tǒng)回收從而引起內(nèi)存泄露和OOM崇决。
個(gè)人感受
自從使用了 MVP 模式材诽,腰不疼了腿不酸了,畢設(shè)作品的項(xiàng)目代碼變得更加規(guī)范簡潔恒傻,后期的維護(hù)也方便很多脸侥。入職后接觸的項(xiàng)目代碼也是用的 MVP,于我而言碌冶,少了許多閱讀障礙湿痢。
對于 MVP 的使用,其實(shí)也沒必要按步驟寫的那樣扑庞,必須定義那么多接口譬重。比起學(xué)會(huì)如何使用,更重要的是掌握它的核心思想罐氨,融會(huì)貫通漫雕。無論是 MVC、MVP破衔、MVVM屈糊,事實(shí)上都沒有絕對的孰優(yōu)孰劣,要根據(jù)實(shí)際的項(xiàng)目進(jìn)行考量租悄,有時(shí)候甚至可以多種模式混用(假若有多個(gè)不同業(yè)務(wù)模塊)谨究。總而言之泣棋,具體問題具體分析胶哲。唯一可以確定的是,選擇了適合的架構(gòu)模式潭辈,后期的開發(fā)之路就會(huì)平坦很多鸯屿。