再談設計和編碼

作者: 人月神話  發布時間: 2015-10-30 09:24  閱讀: 12422 次  推薦: 22   原文鏈接   [收藏]  

  這篇文章談下編碼中的一些細節問題,在談之前首先還是要繼續推薦下《代碼大全》這本書,該書在很多公司都會被推薦為新人入職后的必讀書,即使在互聯網和敏捷開發環境下,該書對于新人仍然有仔細閱讀和體會的必要,要明白架構和設計思維的基礎仍然是編碼思維,面向對象思維的基礎仍然是代碼本身的邏輯和結構,如果不能寫出高質量的代碼,那么架構和面向對象思想很多內容往往難以真正落地。

  對于這篇文章,結合一個最簡單的業務功能場景來講解,即采購訂單新增這個業務功能和場景。

  拿到這個功能需求后當然是首先要詳細閱讀具體的業務功能需求,在閱讀完后第一需要思考的不是業務流程,不是數據庫表,而是首先應該思考該功能對應的核心領域對象究竟是什么?以這個功能為例,可以很明細的看到核心對象是采購訂單,這是一個有明確業務含義的對象,可以看到后續很多的業務操作和方法都將圍繞采購訂單這個對象展開。核心主對象明確后再分析該功能需要依賴的附屬對象,比如:

  • 供應商:基礎主數據對象,依賴原因是需要在創建采購訂單的時候選擇供應商
  • 采購類型:數據字典類對象,依賴原因同樣需要在創建訂單的時候選擇
  • 訂購商品:基礎主數據對象,一張訂單在創建的時候都必須要選擇購買的一個或多個商品和數量信息

  該問題做了基本的思考后,再回答我們具體使用的語言和開發框架,比如基于標準的java ssh框架來開發該功能,要明白我們一般選擇了某種框架后基本的分層思路就已經確定了。當我們拿到一個現成的框架后,在基于這種框架做完練習后,首先要思考的就是這種框架本身的運行機制是如何的?各層之間的調用邏輯和分工是如何的?各層有哪些約束和邊界,在業務實現過程中涉及到的業務規則,邏輯和數據處理究竟應該放在哪層去實現。這些問題必須在編碼中搞清楚,而不是依葫蘆畫瓢而不知其所以然。

  對于我們使用的分層框架基本運行機制和邏輯了解清楚后,基于拿到業務功能需求要開始思考的就不是框架和分層的問題了,而是我們應該在每個分層中設計哪些類?每個類應該有哪些核心方法?類和類之間如何銜接和實現內部調用。單表功能做多了最容易犯錯的地方就是完全的數據庫表式思維模式,即任何一個數據庫表都會有對應的展現頁面,控制類,service類和dao類,完全一對一映射和調用。在這種思維模式下忽略了最前面思考的對象的本質,在領域模型里面一個核心就是我們關心的是有明確業務含義的對象,而不是數據庫表。數據庫表和dao層只是在最終持久化要做的事情而已。

  基于上面的分析可以看到,對于訂單創建的時候供應商信息的獲取應該是供應商類完成的職責,對于采購類型可能則是一個數據字典的common類完成的職責。而對于核心的訂單類可以看到,我們需要的領域服務或方法很簡單,即:

public void savePurchaseOrder(COrderEntity order); 

  訂單應該做為一個核心的領域對象來處理,但是實際在后臺操作則涉及到OrderHeader和OrderDetail兩張數據庫表,任何一張訂單的保存都涉及到對兩張數據庫表的操作。可以看到如果對應的業務功能沒有明確的對訂單明細的操作規則和方法,那么在Service層沒有必要根據頭和明細定義兩個Service類,只需要一個OrderService類即可。在該類中實現所有的訂單保存前數據準備和邏輯校驗。

  在這一個步驟想清楚后,即對于訂單新增功能可以看到,即在頁面和展現層進行新增訂單操作,在點擊保存按鈕的時候應該將頁面上的訂單數據信息傳遞到action層進行基本的數據完整性校驗,然后再將訂單數據以json或已經實例化好的訂單實體類傳遞到邏輯層的savePurchaseOrder方法去處理。而savePurchaseOrder要做的事情就是進行數據準備和轉換,再調用Dao層的訂單頭和訂單明細保存方法進行數據保存,并控制好事務處理。

  基于該邏輯思路我們就基本可以寫出各個分層的核心方法和方法實現,實現最基本的訂單保存功能。在整個過程中我們始終圍繞訂單這個核心對象展開。邏輯層的savePurchaseOrder這個方法是核心,即從展現層到此是準備好訂單實例數據,從該層到Dao層是實現數據最終的持久化和事務控制。

  對于業務規則可以分為兩類規則,即一類規則是數據參考完整性規則,比如數據類型,數據的長度,2個數據屬性間簡單控制邏輯(比如訂單類型為A時候,發運地址必須輸入等);還有一類即較為復雜的數據處理規則或需要調用后臺數據庫實現的控制邏輯(訂單總金額>1000的時候用戶信用等級必須>B級,在訂單保存前還需要再實時檢查商品庫存信息是否足夠等)。對于兩類規則的基本原則就是第一類可以在action層實現,而第二類數據則需要在業務邏輯層來實現。

  對于邏輯層的savePurchaseOrder方法最終應該相當簡單,即首先進行業務規則教育,在通過后再調用訂單保存方法進行訂單保存操作。類似如下:

public void savePurchaseOrder(COrderEntity order)
{
    if !validBusinessRule1(order)  return;
    if !validBusinessRule2(order)  return;
   
    //校驗通過后調用訂單保存方法
   saveOrderInfo(order);
} 

  在該實現中可以看到首先進行了子方法的拆分,保持訂單保存方法本身的簡潔和代碼可讀性。其次需要考慮對于拆分的處理規則的方法是否需要拆分到單獨的業務規則類里面。這里要看情況來處理,即對于業務規則本身有比較高的復用性時候最好拆分為單獨的業務規則類來處理(比如在訂單分發或訂單拆分業務功能中仍然需要使用同樣的業務規則,那么規則單獨拆分到類是有必要的),如果功能確實夠簡單也可以考慮不拆分。

  以上都思考完成后基本的框架和設計實現邏輯就都清楚了,下一步則過渡到具體的編碼實現環節。基于任何語言的編碼其核心都是算法和數據結構,而在這兩者里面最基本的又是變量和數據類型,程序控制邏輯(判斷和循環等),變量定義的規范性,數據類型選擇的合理性,控制邏輯代碼的清晰度都將直接影響到編碼本身的健壯性和可讀性,這些最基本的內容才是寫出高質量代碼的關鍵,也是類似《代碼大全》書籍所一直強調的內容。

  代碼本身具備足夠的自解釋性,源代碼的設計核心就是我們的命名,方法的拆分,控制邏輯要清晰,代碼具備足夠的可讀性往往則不再需要過多的注釋。子方法的拆分是另外一個重點,對于子方法的拆分不僅僅是考慮到方法的復用性問題,有時候一兩行代碼也需要拆分,其核心原因包括兩個,一個是拆分后增加了代碼的可讀性,其次是增加代碼的可擴展性,即后續拆分的子方法往往存在規則或邏輯變更和擴展的可能等。

  任何一段代碼本身的結構化和邏輯化是面向對象編程的基礎,而結構化后最直觀的體現就是代碼的可讀性和可維護性。軟件質量的衡量不僅僅是簡單功能的實現,而是在各種非常規場景下的邊界和異常處理能力,因此代碼本身的健壯性是另外一個重點。任何在編碼階段的工作都必須時刻關注這兩個重點,才可能不斷提升最基本的編程思維和編碼能力。

22
0
 
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()