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

方法入參的設計

我們在設計程式的時候,如果使用常規的分層模型,既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的過程,稱作「裝包」。

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

因為這麼做首先是每次新增一個介面,就要新建一個class,這種做法本身就違反了程式設計的複用性原則,每個class幾乎都是無法複用的。

每個介面,每次請求,都需要做一個多餘的get、set的動作,這個動作,局部看起來也不是很多操作,但是放到一個高併發的環境中,我認為真的代價很高。

從前端接收請求參數時需要創建一個特殊的,只有請求參數的載體物件,資料庫查詢後需要創建一個映射實體的物件,然後介面返回也要再來一個專門的物件,一個請求最少也需要至少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。還有一些欄位必填的自然要做好必填項的校驗。

http://www.cnblogs.com/wuxinzhe/p/6367927.html

arrow
arrow
    文章標籤
    服務端
    全站熱搜

    科技幫 發表在 痞客邦 留言(0) 人氣()