[心得]編寫服務端的相關設計心得及體會


  最近接手公司服務端接口的相關編寫工作,遇到了一些問題,提出了一些想法,討論了一些問題,與項目經理在方案選擇上有了一番爭吵(當然,這種爭吵是家常便飯的事兒)。特此有了一些心得體會。

 

方法入參的設計


 

  我們在設計程序的時候,如果使用常規的分層模型,既Controller、Service、Dao,來對項目進行分層,一定會遇到一個問題,就是不同層及之間,數據傳遞,即每個方法的“入參”應該怎么設計。

  我曾待在一家從事銀行系統開發的公司,項目有一個很大的特點,它並非使用C、S、D來進行分層開發,當然這不是重點,重點是它雖然使用Java,但是大部分不以面向對象的形式來編寫程序,項目中隨處可見的是static的靜態方法,幾乎什么功能都可以用靜態方法來完成,我的理解它已然是一個用java以面向過程的思想開發的項目。這個項目在方法之間傳遞數據時,不適用對象封裝,而是采取多個入參的形式,所以很常見的就是要一個方法,有N多個入參,5個是常見、10個都不特殊。這種方式來編寫方法,有一定的缺點:

  •   在閱讀代碼的時候,經常會不明白參數的意義,而需要仔細的去看javaDoc上的注釋,影響效率;
  •   調用一些工具時,因為不是以面向對象的方式去設計程序,所以所有變量的作用域自然只在方法體內,於是對於一些需要設置參數的工具,簡單的例如:分頁工具,就需要通過一大堆的入參,經常會搞不懂入參的意義,每次調用起來需要重新設置一大堆的參數值;或者使用一個Map來裝載這些參數,這樣帶來的問題就是你甚至不知道有什么參數需要設置,經常會出現錯誤。

  當然,會用這種方式來設計程序,當初項目經理的意思是說:由於變量的作用域僅僅存在於方法體內,當方法執行結束以后,變量就會進入銷毀,能快速的回收內存。雖然我並沒有真正的去測試過這個觀點的正確性,但就我對JVM的GC機制來說,這種做法,是不是會造成大量的垃圾對象,這樣GC就會頻繁被調用來回收這些對象,這反而是增加了內存的消耗。

  有了這次經驗,我自然是非常反對為方法設置大量參數作為入參,以對象作為數據載體,進行傳遞,我認為才是最好的方式。但,依然產生了一些問題。

 

要用什么對象來傳遞參數?


由於種種原因(這里就不需要深究為什么了),我們項目在設計接口的時候出現了一個討論的命題:

  要用什么來接收來自前端的請求數據?又如何把前端想要的數據,按照前端需要的數據接口形式,傳遞出去?

簡單介紹一下會有這個問題的背景:

  我們在設計接口的時候,往往會遇到這種問題:一個數據接口所請求的數據,分別來自於不同的數據庫表,而同時它們也可能在程序中,是分別屬於兩個不同的對象的成員屬性,那這個時候,我們要怎么去設計接口,如何返回數據?    

我先來揭曉一下這個討論結果的最終的決定:

  我們項目因為不希望不同層在傳遞數據是,不知道具體要傳遞的數據內容,所以不希望我們使用Map來作為載體,而盡可能的使用對象來做。這一點我是沒有異議,表示很贊同,因為我曾就深受其害,Map真心不認為是一個適合用於傳參數的載體,當然這也不是一棍子打死,只是最好不要使用。但經理讓我們這么做,新建一個特殊的對象,這個對象沒有任何實體意義,僅僅是為了接口的形式,而去設計的Class,然后把從數據庫中查詢出來的已經映射成實體對象的數據,拆包,根據對應的成員對象再組裝到這個新的對象當中。這句話可能不好理解,我舉一個例子:

  有一個實體對象(我們叫他Class A),它有10個成員屬性,分別是:a、b、c、d.....;但是某個接口僅僅只需要請求這10個成員屬性中的某3個屬性,於是我就要為了這個接口,專門創建一個類(我們叫他:Class B),這個類只有3個成員屬性,沒有錯,Class B 只不過 是Class A的一個子集。

  我們使用的是Mybatis框架,本身查詢出來的數據,就已經是一個實體類,具有最全的屬性,然后我要把這個實體類的每個接口請求的對應屬性都get出來,然后再set到這個新類當中。我把這個get的過程,稱作“拆包”,set的過程,稱作“裝包”。

當然我不認為這是一個好的設計思路。

  1. 因為這么做首先是每次新增一個接口,就要新建一個class,這種做法本身就違反了程序設計的復用性原則,每個class幾乎都是無法復用的。
  2. 每個接口,每次請求,都需要做一個多余的get、set的動作,這個動作,局部看起來也不是很多操作,但是放到一個高並發的環境中,我認為真的代價很高。
  3. 從前端接收請求參數時需要創建一個特殊的,只有請求參數的載體對象,數據庫查詢后需要創建一個映射實體的對象,然后接口返回也要再來一個專門的對象,一個請求最少也需要至少3個對象來完成,一次請求,產生3個對象要等待GC回收。

 

使用實體類作為數據載體


 

  我們之所以會選擇這種方式,也是有原因的,我們是以json形式返回數據,spirngMVC可以添加一個過濾器,把null值的屬性都去掉,所以我提出的觀點自然是通過實體對象來作為數據承載,一次簡單請求由一個實體對象來承載所有的請求數據,並且承載數據庫返回的查詢數據,這樣一次簡單請求自然就不會產生什么多余的對象和不必要的操作,然后通過json的not_null過濾器,把null的屬性過濾掉,這樣接口就干凈簡潔。

  而且我們基本上都是以面向對象的方式來設計程序的,關系型數據庫也基本上是根據實體來設計表結構,所以表和實體的映射非常容易設計,關聯性很強,每次數據傳遞接口也不會很多參數,因為畢竟一次請求所需要的數據,跟他發來的請求條件,都會有一定的關系,所以依據對象之間的嵌套關系,實體對象很容易作為數據載體進行方法之間的傳遞。

  但是,這樣不代表就完全沒有問題了,而這個問題我也不知道如何解決:

  一個對象假設有10個成員屬性,而一次請求,有1個以上,但可能也就是2個或3個數據參數作為查詢條件,那我卻要為了這2-3個參數,去實例化一個具有10個成員屬性的對象,雖然另外的7-8個成員屬性可能沒有值,但是不代表他們不消耗內存。

  這個問題我一直都想不通,是否有可以解決這類問題的辦法?項目重構,將經常調用的參數進行抽象成父類確實能夠緩解一點,但也不能完全避免這種內存的浪費。

  

就算沒有值,也要設置一個默認值來表示


 

  但前端那邊提出了這樣一個問題,有的請求,某些字段確實會有出現沒有值的情況,即null的情況,那就會被過濾掉,結果鍵值都沒有,那我就不知道到底是后端沒有數據,還是程序出錯。於是提出,就算是null,也不能把鍵值去掉。

  其實當時我被這個問題問蒙了,也沒有什么好的解決辦法。但是回頭想一想。。。如果后端返回null,你前端就一定能夠確定,不是程序問題么?如果程序出問題,一樣會出現null的返回結果,數據設置失敗了還不也一樣會值為null。要分清楚這個問題,就要能夠確定這個值是否是“人造”的數據。

  所以我認為,不管是何種數據類型,都應該盡量(同樣不能一棍子打死,有的數據確實沒辦法設置這種默認值)去設置一個默認值來作為這個數據沒有值的一個表示,如時間可以設置為00:00:00,正整數可以設置為-1。還有一些字段必填的自然要做好必填項的校驗。

  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM