邁向架構師的暖身運動(4):不要在路上放一堆石頭,然後來絆自己的腳。

適當的設計,應該是考量各種可能情況,對程式做的具彈性且可重覆使用的軟體設計,除了基本的物件導向規範以外,還要加入一個守門員的角色:規則(Rule)以及驗證器(Validator)。

坊間很多程式設計的書都告訴我們:正確,適當的設計可以加速程式的開發工作,而善用物件導向的方法,可以讓開發的工作量減到很小,透過可重覆使用的特性,來達成軟體組裝的目的。然而,卻沒有告訴我們什麼才是"適當的設計",因為就"適當"這兩個字而言,可能就有十幾種方法論,或是數以百計的設計方法,而且很多的書基本上都有附上範例程式,並且聲稱那是最適當的設計,然後一些 PG 就拿去大量使用,接者就是:

  • 為什麼某支程式莫名奇妙的掛掉了?
  • 為什麼資料庫會被鎖定啊,我什麼都沒做啊?
  • 為什麼會出現 IndexOutOfRangeException?
  • 為什麼數值和我預期的大小不一樣啊?
  • 為什麼寫入的資料是錯的?
  • 為什麼計算出來的數字不對?
  • ...

這些問題在論壇上其實很常見,而且多是引用別人的程式或類別庫來用所致,其根本原因有幾個:

  • 什麼都不懂,連原始碼都不看就拿去用(沒有原始碼的話就殘念了...)。
  • 以為別人的類別庫什麼都做的好好的,連基本的值檢查都不做。
  • 自己撰寫的指令(例如 SQL)寫錯,或是埋下漏洞(例如 SQL Injection)。
  • 沒有既定(公司要求),也沒有自我要求的 Coding Standard 或編程規範。

適當的設計,應該是考量各種可能情況,對程式做的具彈性且可重覆使用的軟體設計,除了基本的物件導向規範以外,還要加入一個守門員的角色:規則(Rule)以及驗證器(Validator)。古人有云:GIGO(垃圾進垃圾出),設計資料流程以及資料存取時,需要特別注意軟體會不會發生 GIGO 的問題,尤其是企業級應用程式,像是 ERP/CRM/SCM 乃至於一般的 POS 或是銷售系統,一個環節的數字不對,輕則影響微量的庫存數,重則影響預算的規劃甚至策略擬定的失誤都有可能,更不用談銀行系統中,多一個零和少一個零的差別了。

沒有適當設定規則的程式,就像在一條大路中,放滿各式各樣的石頭般,在運行時左邊踩一下,右邊踩一下,或是直接絆倒。當程式愈來愈大時,這些石頭可能就會突然變身成地雷,觸之即死,SQL Injection 就是最好的例子,為什麼一個 Mass SQL Injection 破壞力會這麼強?其實是開發人員埋入這些地雷而不自知(某種程度也要怪罪於部份入門書壓根不提這個漏洞的嚴重性... ) ,然後又在嫌變更設計方法會花很大的工,結果就是一個 SQL Injection 破壞了重要的資料,然後到處到論壇中求救之類的 ... 那為什麼不先防範於未然?

規則其實是可大可小的,像是:

  • 值域的限制(例如0-100,或是要求大於或小於指定值)。
  • 資料的限制(例如日期格式,數字格式,字串格式,像身份證字號,電話或銀行帳號資料,信用卡號也有格式)。
  • 結構的限制(例如 XML 的結構或是資料庫的結構)。
  • 流程的限制(這就是一般所看到的 Workflow,每個作業都有一定的流程)。
  • 計算的限制(例如庫存和銷售間計算的公式,或是一般的數學公式)。
  • 存取的限制(即權限)。

而一個規則中可能還隱含其他細部的規則,像是 ATM 交易的規則中會包含怎麼計算 Balance,必需要啟用交易 (transaction) 以及 ATM 執行的流程(像密碼驗證,帳戶驗證,交易後檢查等),這類型的規則通常會稱為商業規則或業務流程(Business Rule),也作為元件內(或元件間)溝通的重要基石。

驗證器(Validator)則是套用規則來檢查資料的工具,這個工具在 ASP.NET 內建有五種(RequiredFieldValidator, CompareValidator, RangeValidator, RegularExpressionValidator 與 CustomValidator),可用來針對輸入端做資料驗證,不過筆者在此所提的驗證器,是埋在類別中的輕巧型驗證器,它可以在計算時期(runtime)針對輸入類別屬性(或私有變數)的資料進行驗證的工具,如此可以在元件交換資料的時候針對資料進行驗證,以防止有不合乎規則的資料流入資料庫(或其他元件)之中。筆者在使用 StackFrame 來取得目前執行中方法的資訊 這篇文章中,已有實作一個可注射在類別屬性中的輕量型驗證器的小工具(IntegerValidationAttribute),這個工具使用了 Reflection 與 Attribute 兩種 .NET Framework 可控制類別行為資訊的功能來實作,讀者可舉一反三。

在元件中設定好規則後,那等於是將石頭自路上移開,而程式變大時,因為規則不變,會讓它變成地雷的可能性也降低到微乎其微(就算需求改變需要重設值域也不會有太大影響),因此不論在撰寫或設計程式時,適當的管理使用的資料,讓它可合乎規則,可以降低未預期的問題發生的可能性,也會在系統變大時不致為了維護而疲於奔命。

參考文獻: