標籤:

好書一起讀(186):編程的一點心得

最近改自家產品,趁機引入了些技術,尤其是好久沒碰的Hibernate,用著感覺還是有其方便之處。另外也試了試流行的OPOA + Rest介面的方式,確實邏輯更簡單,工作量也更小。

還把Bootstrap、Foundation、AmazeUI,以及Angular、React、BackBone、Vue都用了一遍,追一追前端的潮流。

是時候把個人的一些習慣和原則記下來了,攤子太大,時間長不用的點放腦子裡不可靠,先草草記記,陸續補充,補充得差不多了再整理。

裡面有些是社區中廣受認可的最佳實踐,更多的是我個人的野路子,用著還算順手,僅供參考。

1.實體類使用約定式的類欄位-表欄位映射策略,即類欄位駝峰myValue,表欄位小寫字母下劃線my_value,這樣就不用每個欄位上都寫註解指明列名了。

2.不使用級聯的增、刪、改,涉及到多個表時,編程地使用多個save/delete,每個save/delete隻影響到一個表。

3.不使用急載入,永遠使用懶載入,以讓查詢語句盡量少。

4.每個實體都只有與資料庫對應的欄位及一對多、多對一、多對多欄位,不使用DTO性質的欄位,保證是乾淨的實體類。

5.在RestController中進行DTO類與Entity的互相轉化,DTO類與Entity類型不同的欄位,要保證名稱不同,用編程的方式互相賦值,只有類型相同的欄位才名稱相同。

6.DTO類中邏輯與Entity類欄位相關、但名稱不同的欄位,以例如Str/DTOs/Value/Id結尾,分別用於字典欄位轉為值、一對多欄位轉為多個DTO、字典欄位顯示數、多對一欄位顯示主鍵。所以Entity類就不要以這些單詞結尾,以避免混淆。如果是要同時用MyBatis處理的實體,xxId難以避免,這時DTO就不要聲明xxId。

7.最優先考慮最簡單的、基類即提供的findOne/findAll查詢,如不能實現功能,則考慮靈活的原型查詢,原型查詢太麻煩,則使用JPA提供的介面方法,最後,使用@Query標籤,最最後,使用MyBatis。

8.也可以考慮1)在持久層返回多餘的數據或2)使用多次查詢,然後在業務層編程地處理以實現邏輯。畢竟持久層沒有承載完整的業務邏輯的義務。

9.表現層的更新方法:用DTO承載參數,查找原對象Entity,將DTO的欄位用BeanMapper.copy方式更新到Entity上,再用編程方式將名稱不同的欄位更新到Entity,這樣就既保證了名稱相同的欄位不用辛苦寫代碼,又保證了名稱不同的欄位能被手動編碼控制修改的邏輯。

10.表現層返回JSON時,返回DTO而不是Entity,這樣能讓兩者分別變化,將Entity用BeanMapper.map的方式生成DTO,即自動帶有兩者名稱相同的欄位,然後使用編程方式,將兩者名稱不同的欄位賦到DTO之上。

11.表現層總是返回ResponseEntity,這樣能方便地指定狀態碼,客戶端通過狀態碼判斷是否成功。當狀態碼大於400時,攜帶錯誤的提示信息。

12.因為事務是加在業務層,而且語義也如此,所以不要在表現層進行超過一條的業務層代碼調用,表現層只負責對請求、響應中數據的「迎來送往」和校驗,業務的事都放到業務層。

13.超類定義的「一般方法」為了有擴展性,應該定義一些方法來允許子類實現(模板-回調模式),如果子類必須實現,則定義為抽象方法,子類實現不實現都可,則定義為空方法體的非抽象方法,無論如何,超類都必須是抽象的,在類繼承中,只有葉子節點是非抽象類。子類實現、重寫的父類的方法,父子類中該方法都用protected。

14.因此一般情況下子類只要在超類提供的擴展點上,實現相應的回調方法即可,但如果情況極特殊,允許子類重寫超類的模板方法,這一招要慎之又慎,最好不用。

15.始終使用事務腳本的面向過程思維。表現、業務、持久諸層組件無狀態,欄位僅限於持有別的組件。Entity、DTO諸對象無行為,方法僅限於1)getter/setter 2)暴露給別人如freemarker用於假裝有某個欄位的假getter(當比較懶不想用DTO的時候,這已是容忍的極限) 3)自己改自己的狀態的方法。Entity和DTO的方法決不允許修改到全局狀態。Java程序里沒有任何狀態,狀態全由關係資料庫、NoSQL資料庫、消息隊列等容器保存,由事務保證一致性,就不用費勁想並發編程的事了。

16.資料庫的表設計,慎之又慎,茲事體大,改它成本太高,一定要謀定後動。表設計好之後,用代碼生成器生成各個層的代碼,再根據需要修改生成的代碼。所以只要設計好表之後不修改表,就會很開心,否則就會很煩。

17.Web交互用整頁提交還是OPOA?如果有App或者有做App的打算,考慮到後台代碼的工作量,網頁設計最好與App設計在業務上達成一致,這樣後台暴露的rest介面就能復用了。

18.rest介面的數據校驗是username-token方式,而網頁的登錄狀態是session方式,所以用shiro定義不同的Token和Realm,保證rest的Controller上可以寫shiro的@RequireRole等標籤。

19.移動App的httpClient,總是攜帶username/token,收到服務端返回的響應時,先檢查狀態碼是否成功,如失敗則提示錯誤信息,成功則繼續邏輯。

20.移動App的WebView中的網頁,網頁的JavaScript代碼從App中獲取username/token,發送Ajax請求時攜帶它們。這樣後端對httpClient和WebView的Ajax就一視同仁,甚至對OPOA的PC網頁也可以如是,將username/token存到網頁的storage里,發送請求的時候攜帶它們。

21.如果網頁使用響應式CSS框架,如Bootstrap、Foundation、AmazeUI之類,PC和移動瀏覽器即可同用一個網頁,考慮到這些框架原生的樣式都比較樸素,用於生產還是要用到基於它們的高一層封裝,如Metronic。這方面Bootstrap有優勢。

22.網頁使用的MV某框架,如Angular、React、BackBone、Vue之類,同時考慮到先進性和低侵入性,目前看Vue較好。當用於後台管理,不少CRUD代碼是相似的,可以抽出去放到一個js里,把約定做好,網頁就只需寫模板了。考慮到畢竟各個頁面各有不同,這裡的JavaScript代碼也要好好的封裝,可以把封裝後台表現層的思想用在這裡,把修改/擴展的點設計好。

23.OPOA的頁面初始化,考慮到HTTP請求盡量少,可以用一個Ajax請求獲得足夠的數據,或者用多個請求,為其中一部分請求使用相當長時間的緩存,即那些不經常變的數據。當這種數據變了,就要用一個新的URL來請求,以強制刷新緩存。這樣就能讓實際發生的請求數量少,且交互數據小。除了必不可少的交互外,一律不交互,節省客戶端的帶寬,減少等待時間,也降低服務端的壓力。

24.非OPOA的網頁,體積夠大的HTML無論如何都要傳輸,但js/css/img等仍然可以緩存,壓縮混淆看情況用。而網頁,可以在伺服器做靜態化,或者頁面緩存,避免顯示首頁時總要查資料庫,網頁允許多久的「不最新」時間根據業務而定。


推薦閱讀:

計算機語言恐懼症怎麼辦? 或者說,一學習就困的病怎麼治?
做計算幾何有前途嗎?
SegmentFault 技術周刊 Vol.5 - Docker丨Build, Ship, Run, and Monitor
在linux中做.NET開發是怎樣一種體驗?
如何用計算幾何模型畫一束玫瑰花?

TAG:编程 |