借一個項目談Android應用軟件架構,你還在套用MVP 或MVVM嗎

   在《Android開發進階,從小工到專家》一書的第26頁中有這麼一段話,說Android之父Andy Rubin在被採訪時說過,在設計Android之初他就希望Android能像FaceBook那樣可以使用不同的應用中的功能模塊兒,通過現有的模塊兒像搭積木一樣方便地構建一個應用。正式基於這一理念,Android被設計爲高度組件化、可複用的系統。

在Android的應用開發中,目前流行MVP和MVVM的軟件架構風格,讓UI層和業務邏輯層,數據層充分解耦,充分分離。這本來是好事,本文要探討的本意非是摒棄MVP或MVVM架構,而是意在說明,一切都是根據需要爲目的,而不是像做物理題一樣的套用公式。意在突出一種模塊化和組件化的軟件開發思想,助力敏捷開發,大幅度的提高開發效率和穩定性。站在模塊化思想的思維角度上是思考和解決問題。而實際的本質上,也是做到了UI和業務邏輯的分離。只是看問題的維度不同。相比之下模塊化則是更進一步。因爲也能從模塊化的思想中找到MVC的影子,顯示模塊就是V,業務模塊就是M。 只是模塊化思想來的更細,更容易複用,更實用。M可看做爲,又進一步細化的不同的業務邏輯模塊組成。至於那個C嘛,算是鏈接V和M的橋樑。

    金庸武俠小說《笑傲江湖》中,獨孤老前輩教令狐沖武功時,教他說忘掉之前那種種花枝招展的帥氣姿勢吧,別忘了你的目的是什麼,只有能夠打敗對手就是好招。再借用一句馬列主義的哲學來說,實踐是檢驗真理的唯一標準。用鄧大人的話通俗點兒來說,不管黑貓白毛,能抓得住老鼠就是好貓。意思都是說要告訴我們一是思想不要過於固化和死板,二是真理來自於實踐中,而非盡信專家或權威,或是人云亦云。所謂的專家都是有專有家。在家裏(某一領域)比較專注(專一某個領域且達到一定境界)。說以專家的告誡都是有一定道理的,但也並非都是真理。畢竟都是人嘛,都不是神仙。最明顯的例子就比如說在經濟領域,有很多的專家,結果誰都不能百分百預測未來經濟的走向,預知哪隻股票是潛力股。爲什麼?因爲他們各有各的理。真理只能交給時間和實踐來檢驗。

     再來說說流行的MVP或MVVM架構模式的本意。只有站在更高的維度上去看問題,才能看透問題的本質,否則就會是“不識廬山真面目,只緣身在此山中”。先說如果沒有MVP和MVVM這種架構風格之前是什麼樣,再說它的出現解決了什麼問題,有什麼好處,以及爲什麼要用吧。在Google剛推出android應用開發時,第一批喫螃蟹的人,也就是最早的佈道者,大都是看官方文檔或歷程一路摸爬滾打,總結而來的經驗。常規和簡單的實現業務應用的辦法是直接在activity裏直接寫代碼,因爲直觀且清晰嘛。但是隨着業務越做越大,越做越複雜,發現一個activity(UI層)裏好幾千的代碼,看着都費勁,更別提讓別人維護了。還有就是換了一個項目,UI變動了,但原來寫好的業務都在UI裏,從頭再做一遍嗎?原來做過的有,就費事兒點兒一點點摳出來吧,累死個人了。你說他複用代碼了嗎?確實複用了,但是很累很繁瑣,把原來的業務抽離出來不亞於從頭重新實現了一遍業務,且很容易出錯。這樣的業務充斥在UI層中到處穿插,不但邏輯不清,複用困難,且容易導致各種各樣的問題。於是MVP模式出現了。MVP中的p就像箇中間人。把M數據模型層或業務層跟這個V,UI層徹底隔離了。無論你的業務是操作數據庫或是獲取網絡數據再怎麼加工處理,要想表達給UI層,只能通過P這個中間人去做。這樣的好處,一方面業務邏輯清晰,分工明確。一方面利於更大限度的複用。

  但是大道理人都懂,你要是硬是去這麼的套公式吧,有時候也很累很糾結。且往往掌握不好度,比如過度的設計,到處是接口,寫起來也很累。比如業務邏輯層是放在model層呢還是P層呢,model層是不就是指存取或讀取網路或本地數據呀。沒人能準確的分清,於是不糾結了咋樣順咋樣來,不去較真兒了。

  前面說了一堆廢話,現在進入正題。

      先拋出來一個問題,如何在兩週內實現一款Android的POS機應用?有刷卡,有語音,有存儲,有界面,有通信,有業務。

       是的,沒錯,是兩週的時間功能完整的做出來。

  再思考一個問題,平時小朋友們都愛玩一種搭積木的遊戲,爲什麼他們能夠僅憑自己的想象力就能創造出那麼多的模型?有橋樑、有大門、有城堡、有汽車、有飛機,想到什麼就能快速的搭建起來?其實就是模塊化的思想。可惜我小時候只玩過泥巴,想來現在的小朋友們應該更聰明吧。不用管這積木是誰造的,怎麼造的,用的什麼料。只管搭建自己想要的模型即可。那麼,回過頭來看我們的軟件工程項目開發,何嘗靈感不是取自於大自然,取自於日常生活。就比如建造房子,你會去想一塊兒磚,一扇窗戶是怎麼造的嗎?每個人各有分工,角色不同,分工不同,共同有序的構建起整個大廈。同樣的磚頭,放在城市裏是建築,放在農村裏可以是牆頭。放在我桌子下面,可以墊桌子腿。放到美女口袋裏,可以當防色狼武器。同樣的模塊可以用在各個地方去。根據你的需求,哪有需要往哪搬。

  到這裏真正引申出本文的主題,即採用模塊化的思想去考慮我們的Android應用,而非死板的套用MVP或MVVM風格。以模塊化和組件化的思想去考慮問題,是站在了更高一層的維度上去思考問題。管你是什麼設備,什麼型號機器,我的本意就是實現這麼一套功能。根據需求,把我們的應用拆分爲不同的組件和模塊。在Android這種機器中,界面,即activity,即MVP中的V視圖層,只是我應用中的一個顯示UI顯示模塊兒而已。既然只是佔比很小的一個顯示模塊兒而已,註定業務不會在activity裏去寫去實現。

  按照模塊化的思想來劃分的話,一款Android的pos機無非有以下幾大模塊功能組成而已。

界面UI顯示模塊,記錄存儲模塊,參數配置文件操作模塊,通信模塊,語音播報模塊,卡操作模塊,算法模塊(常用加密算法或工具類)等幾大模塊組成。然後基於這些模塊,再在這些模塊的上層拆分和建立幾個任務去銜接和組織業務。如卡處理任務線程,通信任務線程,定時任務線程等。再利用Android的事件總線通信機制,如rxbus或Eventbus等,把具體業務和UI模塊(activity)銜接起來。這不就是完整的應用嗎?且結構清晰,分工清晰,全都是模塊兒化,一層層的往上搭建應用,是不是很像是搭積木?也完全符合設計模式的幾大原則,做到高內聚底耦合。單一職責,接口隔離,開放封閉,迪米特法則。構建的這些基礎模塊之間,做到相互的獨立和無依賴,就最大限度的提高了複用性和穩定性。且結構和邏輯,層次清晰。那麼基於這些模塊構建的應用之間如何傳遞數據呢,看下面的圖,更清晰直觀點兒。

卡操作模塊、通信模塊、存儲模塊、語音模塊、常用算法或類庫等,這些作爲底層的基礎技術組件用。這些基本都是能夠複用的功能獨立的模塊。

然後在這些可複用的技術組件之上是業務的組織。稱之爲可複用的業務組件層。模塊化的思想有兩種,不但是技術組件的模塊化,不同的業務之間也可以把獨立的業務模塊化。目的都是爲了結構職責清晰,更大限度的複用,提高穩定性和生產力。比如可以封裝出不同的業務模塊,交通部卡處理模塊,自發卡處理業務模塊,二維碼業務處理模塊,小鍵盤處理模塊,消費記錄通信組包和解析等通信業務模塊。這些都是可複用的業務組件。那麼上層的卡任務和通信任務等可以調用這些業務模塊完成整個業務邏輯。

在這些業務模塊之上又創建了幾個線程任務去組織和銜接業務,各幹個的活。跟卡相關的都在卡任務線程裏。跟後臺通信上傳記錄和下放參數相關的都在通信線程任務裏,需要定時執行的一些其他任務在定時任務線程裏。這部分可以看做是業務的大體框架。

最上層就是最接近用戶的UI顯示層了。需要更新UI時,業務框架層只需要通過事件消息總線技術,把事件通知給UI層處理即可。在UI層可以完成界面展示,頁面跳轉和切換,以及用戶觸摸事件等簡單的業務邏輯處理。

最後在說下,底層的業務和上層UI,交互的數據從何二來?如何交互,如何傳遞數據呢,全局的靜態類或JavaBean就派上了用場,他們相當於全局變量一樣,負責收集和獲取數據。至於這數據怎麼個顯示給UI,何時觸發,由業務邏輯層負責觸發事件,通過消息總線去通知UI更新或顯示,更新或顯示的內容在全局的靜態類裏。

這種模式,如果非要向MVP和MVVM的風格架構上套,它哪個都不是。好像是隻有V和M,其他的找不到了。但業務沒在V中。總之不要去糾結這究竟是什麼路數了。因爲一開始考慮問題和看問題的角度就不同,是按照模塊兒化思想考慮的。activity只是模塊化中的UI模塊而已。所以還是不要去往MVP或MVVM上想,不是一回事,忘掉他們吧。

  有了這些之後,大體框架已經有了,接來下就可以專注於業務實現啦。豈不是很簡單。兩週的時間去完成一些基礎業務也是綽綽有餘的。但是前提是,建立在這些可複用的技術組件之上。比如通信模塊,記錄存儲模塊。記錄存儲模塊本來是操作數據庫,但是封裝後讓你看出來任何數據庫操作的影子。只有save和read,delet等簡單的接口。把數據模塊封裝的就像是操作快遞存儲櫃,應用的人只需要知道存東西,存在哪。取東西,東西在哪即可。管你內部是操作的oracle還是MySQL還是sqllite,好的封裝儘可能做到迪米特法則。最好讓使用者不用去關注你的底層是用什麼實現的。

  所以公司新的Android的pos機,我幾乎只用了兩週時間就搞出來了。是的,兩週,這沒有開玩笑。

當然這得益於之前711機器累積的經驗,提前實現了記錄存儲模塊,通信模塊和配置文件操作模塊。同時多虧同事的幫忙,分工合作,同事提供卡操作模塊的操作卡片的底層接口,美工提供了UI素材。沒有這些,一個月也搞不定啊。但是存儲模塊,由於涉及到記錄安全,顯示尤其重要。不經過壓測是萬萬不行的,得確保無一條數據丟失。有的說存儲數據多簡單啊,操作數據庫一個 insert指令就完了的事。但是我想說的存儲模塊是包含了業務的實現。有哪些業務呢,首先,不用再考慮如何建表結構,數據不會一直往下存儲,終端機器裏敢一直這麼傻瓜式的存下去,機器早晚要存滿掛掉。記錄上送至後臺一條,要刪除一條,可不是真正的刪除啊, 那麼豈不很不安全。只是清掉更新標記,即記錄是循環覆蓋的模式存儲。這裏面涉及不少業務邏輯,可不是簡單的insert和delte就完事了。那麼這樣封裝後使用起來有多簡單呢,可以說是傻瓜式操作,誰都會用。即便不知道SQL是啥玩意兒的都能玩的轉。保存數據直接 save( data內容)即可。 data內容爲要組織的記錄的二進制數據。有的說那要強大的sql有啥用,你當成文件存儲來用了嗎。sql和數據庫的優勢木發揮出來。這裏我只想說SQL是強大,但是你會用高射炮去打蚊子嗎。滿足需要纔是目的。我在終端上壓根用不到負複雜的查詢。要保存的記錄字段有很多,且終端需求上經常多變,會爲了哪天需求要求多傳個字段,就去動一動表結構嗎?太沒必要了。那樣就太累了。還是那句話,實踐出真知,不信擼起袖子來幹,不服來戰。可以比一下你用操作SQL快還是我經過封裝過之後的使用快。我這讀寫和操作記錄只需要幾分鐘。但是你要提出來給你個卡號,給我找出來他的所有明細,且金額低於10塊的,再統計出總額,再關聯查詢下是是屬於哪個司機消費的,那麼不好意思,這就得花點兒時間了。不像你一個SQL語句分分鐘鍾搞定了。但是終端上沒有這種需求。即便有,再封裝出個接口就行了。內部實現我也用SQL,它的強大是無疑的。

再舉例說下配置文件存儲模塊的封裝,假如有個需求讓你存儲配置信息如 IP和端口 port到配置文件中。你操作完成這些如何做,需要多久?我給你說我需要30秒足夠了。這這取決於我打字的快慢。無論你是操作ini文件或是json文件或是xml文件,再怎麼也需要額外好多步吧。就拿Android中的 SharedPreference類來說,那麼完成這些操作,你需要:

SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);

String ip = "218.28.111.121";

int port  = 5050;
sp.putString("IP",ip);

sp.putInt("port",port);

存儲完開機要用到的話,還得一個個的加載出來。比如

String ip = sp.getString("ip","");

而我只需要 

syscfg.ip = "218.28.133.181";

syscfg.port = 22288;

syscfg.save();即可完成存儲。開機後,只需要syscfg.load()即可完成加載。

從這些數據存儲量小時,看不到優勢多明顯。如果有幾個個參數要存儲和保存呢。優勢就出來了。

比如終端要保存票價信息,參數太多了。
 dealCfg.ver = 12;
 dealCfg.time = "201910251551";
 dealCfg.discInfo[0].cardType = 5;
 dealCfg.discInfo[0].purseDisc = 100;
 dealCfg.discInfo[1].cardType = 6;
 dealCfg.discInfo[1].purseDisc = 200;
 dealCfg.saveCfg();即可。

最後再來說下複用性。

假如我有另外一項目,也是Android系統,但卡處理部分硬件變了。那麼上述只需要改動卡操作模塊。其他完全不變,不受影響。假如有另外一Android項目,不涉及到讀卡。那麼通信模塊和存儲模塊和工具類模塊則是直接可以拿走用的。

如果,想在電腦上實現這麼一款POS機咋辦?

那麼業務部分是不需要動的,卡操作模塊改變下,換成操作讀卡器模式。界面部分,則可以替換爲其他的UI或者網頁也可以。比如用前端技術Vue或reaect構建出界面作爲UI層。使用websocket和卡處理任務交互起來,卡交易成功後通過websocket通知到前端瀏覽器,javascirpt操作數據去更新UI。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章