Android開(kāi)發(fā)過(guò)程中拒迅,我們或許見(jiàn)過(guò)這樣的現(xiàn)象:
1. 上帝類(lèi)頻頻出現(xiàn),有些類(lèi)可能包含多個(gè)功能模塊的代碼
2. 臃腫類(lèi)數(shù)不勝數(shù)她倘, 很多類(lèi)里面可能少則1000行璧微,多則幾千行的代碼
3. 不同功能混雜,類(lèi)中包含不同層次的功能硬梁,難以查詢(xún)邏輯主線
4. 無(wú)節(jié)操的代碼重疊
以上現(xiàn)象前硫,在我們開(kāi)發(fā)中,尤其是接手別人代碼時(shí)荧止,會(huì)顯示痛不欲生屹电;那么我們來(lái)看一下上述現(xiàn)象的弊端有哪些 ?
1. 代碼查找 想一下找出需要的代碼跃巡,難危号!哪怕ctrl + F查找,都要分析好久4膳凇4猩!
2. 邏輯交叉混亂 想改什么東西娘香,實(shí)在鼓不起勇氣苍狰!
3. 不同代碼齊聚一堂 鬼才知道添加或修改一個(gè)功能都要改多少地方,何況在不了解需? ? 求的前提下
如果頻頻遇到以上現(xiàn)象烘绽,或許我們就應(yīng)該想了淋昭,到底什么樣的代碼,才是所謂的好代碼呢安接? 有沒(méi)有什么量化的標(biāo)準(zhǔn) 翔忽? 又如何來(lái)很好的避免上述的問(wèn)題 ?
答案是**規(guī)范**與**標(biāo)準(zhǔn)設(shè)計(jì)**
首先談一下規(guī)范,不詳談歇式,不矯情驶悟,建議參考Alibaba推出的Java開(kāi)發(fā)規(guī)范,請(qǐng)走這里:
? 這里主要詳談的是設(shè)計(jì)材失,首先來(lái)談一下開(kāi)發(fā)出高內(nèi)聚痕鳍,低耦合,層次分明龙巨,可讀性高的代碼笼呆,需要怎么做 ?
? **1. 分清代碼職能**
? 想要添加一段代碼旨别,首先必須明確這段代碼是用來(lái)做什么诗赌, 這段代碼的功能是什么類(lèi)型的功能 ? 這是一段高度反映業(yè)務(wù)的代碼秸弛?或者是可復(fù)用型的獨(dú)立業(yè)務(wù)功能代碼铭若?又或是與業(yè)務(wù)完全獨(dú)立的常規(guī)輔助類(lèi)型的代碼? 只有明晰了這段代碼的功能與類(lèi)型递览,才能映射出更好的設(shè)計(jì)奥喻。
? **2. 初步劃分模塊**
? 模塊劃分,是系統(tǒng)源碼合理劃分的最有效的途徑之一非迹。但模塊的劃分不是必須的,項(xiàng)目越大纯趋,模塊劃分帶來(lái)的益處越大憎兽;同時(shí)關(guān)注點(diǎn)的劃分也越難。小規(guī)模的項(xiàng)目吵冒,完全可以越過(guò)這一步纯命。
? **3. 初步層次劃分**
? 如果說(shuō),模塊的劃分可以把一個(gè)大的系統(tǒng)有效地劃分為幾個(gè)子系統(tǒng)痹栖;那么針對(duì)不同的子系統(tǒng)亿汞,同樣包含有大量的代碼;因此層次劃分的重要性就不言而喻了揪阿;至于這個(gè)的使用疗我,根據(jù)業(yè)務(wù)進(jìn)行自我分析判斷;一般而言南捂,層次劃分是必要的吴裤,如當(dāng)前流行的MVC, MVP, MVVM等框架
? **4. 功能屬性劃分**
? 和1類(lèi)似,我們需要明白一點(diǎn)溺健,我們的代碼有幾種類(lèi)型麦牺? 總體來(lái)區(qū)分,分為Common與Business;我感覺(jué)Business下還可以進(jìn)行劃分,有些Business是需要復(fù)用剖膳,不同模塊都需要的魏颓, 我稱(chēng)之為**弱業(yè)務(wù)型**;有些Business代碼沒(méi)法重用吱晒,我稱(chēng)之為**強(qiáng)業(yè)務(wù)型**甸饱。因此,我的功能劃分包括: **通用型**枕荞, **弱業(yè)務(wù)型**以及**強(qiáng)業(yè)務(wù)型**柜候。
? **5. 單一職責(zé)原則**
? 單一職責(zé)原則,作為面向?qū)ο蟮?大設(shè)計(jì)原則之一躏精,極大的保證了不會(huì)產(chǎn)生邏輯交叉混亂與上帝類(lèi)渣刷。也是日常開(kāi)發(fā)中必須遵守的原則。在阿里巴巴的**《Java開(kāi)發(fā)手冊(cè)》**中矗烛,也提到了這點(diǎn)辅柴,概括來(lái)說(shuō),就是保證大到每一個(gè)子系統(tǒng)瞭吃,每一個(gè)模塊碌嘀,每一個(gè)分包,小到每一個(gè)類(lèi)歪架,每一個(gè)方法都僅執(zhí)行單一的功能股冗;這個(gè)功能也就是關(guān)注點(diǎn)的劃分,需要根據(jù)開(kāi)發(fā)經(jīng)驗(yàn)來(lái)區(qū)分和蚪,模塊的劃分與方法的劃分必然是不同的止状,而且相去甚遠(yuǎn)。
? **6.? 復(fù)用性與擴(kuò)展性**
? 代碼復(fù)用性攒霹, 相信每一個(gè)人都對(duì)這個(gè)原則有深刻的理解怯疤;復(fù)用性可以極大的提高開(kāi)發(fā)效率, 當(dāng)然必須要配合擴(kuò)展性催束,才能更好的達(dá)到目的集峦。擴(kuò)展性越強(qiáng),復(fù)用范圍就越廣抠刺,該模塊或方法就越具有原子性塔淤。
? **7. 封裝性**
? 在一個(gè)頁(yè)面中,我們可能需要實(shí)現(xiàn)形形色色的需求速妖;如果把這些需求都放在Activity中凯沪,那么可能最后Activity的規(guī)模就可想而知了;除了通過(guò)分層把不屬于Activity的邏輯劃分出去(如MVP中將邏輯放到了P),余下一些的View實(shí)現(xiàn)买优,也需要大量的代碼來(lái)構(gòu)建妨马;這時(shí)候我們可以再次劃分不同的模塊挺举,如TitleBar我們可以根據(jù)業(yè)務(wù)定義自己的TitleBar, 只暴露一些基本的配置,如setTitle()等烘跺;然而有時(shí)TitleBar中也會(huì)包含大量的業(yè)務(wù)邏輯湘纵,這時(shí)我們可以在TitleBar UI層封裝的基礎(chǔ)上,加入一個(gè)TitleBarDao來(lái)處理TitleBar相關(guān)的實(shí)現(xiàn)滤淳,將這塊邏輯繼續(xù)分發(fā)到TitleBarDao中梧喷。
? ? 以上回答了如何更好的設(shè)計(jì)一段代碼,才能更好的保證其邏輯層次的清晰脖咐, 解耦性以及可讀性铺敌,都是個(gè)人愚見(jiàn);代碼設(shè)計(jì)是根據(jù)不同業(yè)務(wù)屁擅,不同規(guī)模進(jìn)行合時(shí)宜的設(shè)計(jì)偿凭,萬(wàn)不可為了設(shè)計(jì)而造成過(guò)度設(shè)計(jì)。設(shè)計(jì)的目的是提高開(kāi)發(fā)效率派歌,提高維護(hù)與測(cè)試效率弯囊。說(shuō)了這么多,如何來(lái)實(shí)現(xiàn)呢 胶果? 以下是一些簡(jiǎn)單的實(shí)例匾嘱。
? 1) 添加一個(gè)View,View顯示的寬度是屏幕的3/4
? ? View targetView;
? ? LayoutParams lp = targetView.getLayoutParams();
? ? lp = 3 * ...獲取屏幕寬度 / 4;
? ? targetView.setLayoutParams(lp);
? 以上這段代碼早抠,很簡(jiǎn)單霎烙,相信很多人也寫(xiě)過(guò);這段代碼首先我們分析功能實(shí)現(xiàn): View寬度的設(shè)計(jì) 和屏幕寬度獲取; 這是一段通用型的代碼蕊连,因此我們可以放在通用層中吼过,方法內(nèi)部包含屏幕寬度獲取的實(shí)現(xiàn),因此根據(jù)單一職責(zé)原則咪奖,我們可以將之抽取出來(lái)。
? 2) string, dimen, style文件
? 這些文件的使用酱床,就是為了將文本, 尺寸, 樣式等抽取出來(lái)羊赵,實(shí)現(xiàn)單一的目的。
? 3) 組件化扇谣,插件化
? 這兩種架構(gòu)方式是大系統(tǒng)規(guī)模的代碼廣義層面的架構(gòu)方式昧捷,將一個(gè)大系統(tǒng)分為若干子系統(tǒng),子模塊來(lái)管理罐寨;
? 4) MVC, MVP, MVVM
? 這些架構(gòu)方式靡挥,則屬于從小的層級(jí)進(jìn)行代碼分層劃分,劃分為不同的層次鸯绿,每個(gè)層次實(shí)現(xiàn)單一職責(zé)跋破。
說(shuō)了這么多簸淀,代碼架構(gòu)的目的是什么?
1. 解耦
2. 復(fù)用
3. 可讀性
4. 健壯性
5. 提高并行開(kāi)發(fā)效率
最近出了很多架構(gòu)思想毒返,無(wú)論是最初的MVC, 還是近幾年風(fēng)頭極盛的MVP, Google推出的MVVM,還是系統(tǒng)層面的組件化租幕,模塊化,插件化拧簸;最終遵循的架構(gòu)原則無(wú)非就是三點(diǎn):
1. 橫向劃分模塊
2. 縱向劃分層次
3. 解耦通信
1. 橫向分塊
一個(gè)大的項(xiàng)目劲绪,代碼量是極大的,甚至達(dá)到1G以上盆赤;這時(shí)必須先以大的層級(jí)進(jìn)行劃分贾富,才能有效達(dá)到分離的目的。現(xiàn)在流行的模塊化思想便有了用武之地牺六;模塊化思想主張將一個(gè)系統(tǒng)橫向切分為不同的子系統(tǒng)或者可以稱(chēng)之為模塊颤枪,根據(jù)業(yè)務(wù)與開(kāi)發(fā)需要將工程劃分為Common模塊, Business-01模塊兔乞, Business-02模塊... 等汇鞭,同時(shí)不同的模塊之間從大的層級(jí)實(shí)現(xiàn)職責(zé)單一原則。
2. 模塊之間解耦
?一個(gè)系統(tǒng)劃分為若干子系統(tǒng)庸追,模塊后霍骄,不同的模塊之間存在交叉通信是必然的;那么如何實(shí)現(xiàn)模塊之間解耦呢淡溯?
?組件化的解耦**思路是實(shí)現(xiàn)一套注冊(cè)路由機(jī)制读整,根據(jù)一套統(tǒng)一的注冊(cè)路由系統(tǒng)來(lái)統(tǒng)一實(shí)現(xiàn)跨組件通信,當(dāng)前比較流程的框架有ActivityRouter,? ?ARouter等
插件化的解耦**則直接通過(guò)Android系統(tǒng)提供的Binder機(jī)制來(lái)進(jìn)行
3. 縱向分層
從系統(tǒng)層級(jí)做了模塊劃分后咱娶,每個(gè)模塊必然也需要自己的架構(gòu)米间,這時(shí)候就需要使用分層的思想了,也就是要說(shuō)的縱向分層策略膘侮。MVC, MVP, MVVM屈糊,F(xiàn)lutter這些設(shè)計(jì)則使用了縱向分層的思想;它們分別將一段代碼或一個(gè)頁(yè)面劃分為3個(gè)層級(jí):
Model層:? ? ? 處理數(shù)據(jù)
View層:? ? ? 處理UI顯示
MVC的Controller, MVP的Presenter, Flutter的Store? ? ?則處理主要邏輯
經(jīng)以上劃分后琼了,數(shù)據(jù)處理逻锐, Ui顯示, 邏輯處理都放到了各自的層進(jìn)行處理雕薪,每個(gè)層僅執(zhí)行本層應(yīng)該做的操作昧诱。分層后,代碼已經(jīng)做到了結(jié)構(gòu)清晰所袁,盡可能的解耦盏档, 易于理解維護(hù)。不過(guò)燥爷,在開(kāi)發(fā)中蜈亩,Model返回的數(shù)據(jù)懦窘,不一定是我們想要的格式; 而且將異步放到Model層勺拣,則Model除了處理數(shù)據(jù)以外奶赠,還要進(jìn)行不同的Async處理。在以上想法下药有,我們可以繼續(xù)劃分:
Model層:? ? 處理數(shù)據(jù)(請(qǐng)求毅戈, 數(shù)據(jù)庫(kù), 緩存等)
Work層:? ? ? 處理異步相關(guān)
Presenter層:? 處理核心控制邏輯
Converter層:? 數(shù)據(jù)轉(zhuǎn)化層 將Model數(shù)據(jù)轉(zhuǎn)化為UI需要的格式
4. 層次間解耦
根據(jù)CleanArchitecture原則愤惰,采用圓蔥形式的分層與解耦策略苇经,因此在設(shè)計(jì)時(shí)注意兩點(diǎn):
1) 明確的層次限定
2) 禁止跨層次調(diào)用
3) 從內(nèi)到外單向調(diào)用
根據(jù)洋蔥結(jié)構(gòu),由內(nèi)到外我們可以得到Model -> Work -> Converter -> Presenter -> UI;在調(diào)用時(shí)宦言,某層只能調(diào)用向內(nèi)的鄰接層扇单,如Presenter只能調(diào)用Work層(存在異步操作時(shí))或Model層(無(wú)異步操作,甚至無(wú)異步操作也可以在Work中封裝一層)奠旺,而不能出現(xiàn)Model層調(diào)用Presenter層的現(xiàn)象蜘澜;相同的,UI層只能調(diào)用Presenter層响疚,而不能跨層調(diào)用Model層鄙信;Converter作為外圍輔助層,不直接參與洋蔥結(jié)構(gòu)
層通信方式
層與層之間的通信忿晕,必然不能通過(guò)直接調(diào)用的方式(特別是View層與Presenter層)装诡,我們可以參考MVP模式中的**接口通信方式**或者Flutter模式中的**Event路由模式**來(lái)實(shí)現(xiàn)。
以登錄功能為例践盼,我們需要以下功能
參數(shù)校驗(yàn)
加載
登錄請(qǐng)求
返回處理
取消加載
顯示結(jié)果
參數(shù)校驗(yàn)鸦采, 參數(shù)的具體信息來(lái)自View層,但是其本身功能為邏輯處理咕幻,因此適合在Presenter層
加載渔伯, 取消加載 涉及到Activity Context來(lái)加載Dialog,因此適合在View層
登錄請(qǐng)求, 毫無(wú)爭(zhēng)議放在Model與Work層肄程,如有參數(shù)轉(zhuǎn)化锣吼,需要加入Converter層
返回處理, 毫無(wú)爭(zhēng)議放在Presenter層
顯示結(jié)果绷耍, 涉及到Toast顯示以及提示,必然在View層
有些實(shí)現(xiàn)可以涉及Context且本身屬于Presenter處理鲜侥,不好區(qū)分褂始;因此建議通過(guò)Context類(lèi)型區(qū)分, 涉及必須使用Activity Context如Toast, Dialog, PopupWindow則在View層描函, AplicationContext亦可的則可以放在Presenter層崎苗。
本篇文章從理論方面講解了Android架構(gòu)一些常用知識(shí)與理念狐粱,且純屬個(gè)人愚見(jiàn),有爭(zhēng)議者可提出一并討論胆数;目的就是找到一個(gè)可以合理解決大部分問(wèn)題且適合移動(dòng)端的開(kāi)發(fā)架構(gòu)肌蜻。