2011-03-31 38 views
1

我的大腦開始受到傷害,所以我決定在這裏問問。驗證入境或保存之前?

我有一個數據對象Employee。 Getters,setter,格式化程序等我有一個管理器EmployeeManager,它負責處理數據庫訪問和其他事情。現在我在EmployeeManager中有一個很大的驗證塊,但我一直在想我是否可以將其中的一些移動到setters。

例如,現在我有;

public function getSSN($bFormatted = true) { 
    return ($bFormatted) ? $this->format_ssn($this->SSN) : $this->SSN; 
} 
public function setSSN($s, $bValidate = false) 
{ 
    // If we're validating user entry, save a copy. 
    // Either way, store a trimmed version. 
    if ($bValidate): $this->SSNToValidate = $s; endif; 
    $this->SSN = str_replace('-', '', $s); 
} 
public function getSSNToValidate() { return $this->SSNToValidate; } 

這樣做是:
*當你設置SSN,如果它被系統(如從數據庫)來完成,然後它setSSN('123456789', false),由於社會安全號碼存儲在數據庫SANS破折號。
*當您從用戶輸入設置SSN時,它只是簡單地setSSN('123-45-6789')然後不僅修剪破折號,而且還存儲原始版本以驗證(因爲我想根據格式進行驗證)
*當您獲得SSN時,如果請求格式化(並且它總是在寫入數據庫時​​除外),它將根據Employee類中的另一個函數進行格式化。

所以我的問題是:我可以在這裏添加驗證到setter,而不是依靠Manager類中的單片驗證功能?因爲我開始必須處理來自整個應用程序的錯誤,所以我決定暫時轉移到一箇中央的Error Handler靜態類,而不是每個管理者維護它自己的錯誤列表。

的,因爲這一點,我可以很容易地添加錯誤處理到這一點:

public function setSSN($s, $bFromUser = false) 
{ 
    if ($bFromUser && !$this->validateSSN($s)) 
    { 
     ErrorHandler::add(array('ssn' => 'Invalid SSN entered')); 
    } 
    else 
    { 
     $this->SSN = str_replace('-', '', $s); 
    } 
} 

所以我想我的問題是:在所有這是否有道理還是我從管理者移動驗證沖水自己(要在需要時還是在寫入數據庫之前執行)到對象(在輸入時執行)?

這是整體通用的,我只是使用SSN作爲一個很好的例子。

回答

3

您應該始終驗證何時在對象中創建對象或設置值。這樣你總是保證有一個處於有效狀態的對象。如果驗證不在對象本身中,則不能保證該對象將被驗證。

實施例的驗證物體的外面:

在這種情況下有一個錯誤,我們忘了驗證SSN。

class EmployeeManager 
{ 
    function saveEmployee($employee) 
    { 
     //Oops, we forgot to validate SSN 
     $db->save($employee); //This is just SQL to persist the employee. 
    } 
} 

//Somewhere else in code... 
$employee = new Employee(); 
$employee->setSSN("FredFlintstone"); //No validation is done here. 
$employeeManager = new EmployeeManager(); 
$employeeManager->saveEmployee($employee); //This call will persist FredFlintstone because validation was missed. 

實施例的驗證物體的內部:

在這種情況下,僱員對象驗證它的所有輸入。這樣我們就知道如果我們有一個僱員的實例,那麼其中的所有數據都是有效的。

class Employee 
{ 
    function setSSN($input) 
    { 
     if(strlen($input) != 9) 
     { 
      throw new Exception('Invalid SSN.'); 
     } 
     //Other validations... 
     $this->ssn = $input; 
    } 
} 

//Somewhere else in code... 
$employee = new Employee(); 
$employee->setSSN("FredFlintstone"); //This call will now throw an exception and prevent us from having a bad object. 
$employeeManager = new EmployeeManager(); 
$employeeManager->saveEmployee($employee); 

此外你不應該讓一個對象被創建,不完全初始化。如果說員工需要SSN,那麼不要提供空構造函數。你的構造函數應該有所有必填字段的參數(與我爲了清晰起見而忽略它們的例子不同)。

+0

是的,我看到其他人說,保持對象的完整性是至關重要的,這意味着不要在其中存儲流浪數據。如果您嘗試添加bum數據,請拒絕它。雖然我也看到了bum用戶輸入(這是)並不例外,但它是預期的,因此您應該毫無例外地處理它。但我認爲,這是更多的語義和編程實踐,而不是這個問題。 – Andrew 2011-03-31 21:38:38

+0

我同意處理用戶數據會很痛苦。在這種情況下,我通常最終會進行兩次驗證:在UI中一次,所以我可以通知他們這個問題,然後再次在對象中。我覺得確保對象是正確的並且驗證可能被調用兩次比讓我最終存儲不良數據的可能性更安全。 – brainimus 2011-03-31 21:45:21

+0

將這個標記爲答案,因爲我已經去了「永遠不要讓你的對象處於有效狀態」的想法。例如,如果我知道SSN將永遠不會被設置爲無效數字(因爲它在設置上得到驗證),那麼我知道我不一定需要稍後再驗證它。我必須學會信任代碼。 – Andrew 2011-04-06 12:57:23

2

基於服務的應用程序的一般經驗法則是驗證應始終在服務器端發生10,在持久性之前不久。或者,您可以在客戶端上執行驗證以獲得更好的用戶體驗。但僅在客戶端執行驗證(即在您的setter中)並不安全。永遠不要依賴客戶數據是正確的。

在進行基於客戶端的驗證時,最好爲每個類(無論是模型,視圖模型,演示者等,取決於您的架構模式)進行自我驗證,而不是依賴某個外部驗證程序。

+0

所以可能在'客戶端'(數據對象)以及插入數據庫之前在管理器中進行驗證。我認爲我的擔心是,我最終會得到兩個ValidateSSN()命令(一個在數據對象中,一個在管理器中),或者我將它外包給一箇中央驗證類...在這裏你是說,不要依賴外部的驗證器?或者我誤解了? – Andrew 2011-03-31 20:47:15

+1

驗證在這方面總是有點棘手 - 在DRO,SRP等各種OO原則之間進行權衡,將相關數據和行爲保持在一起等等。所以回答這個問題並不簡單。在客戶端和服務器之間看到驗證邏輯有些重複(通常客戶端對於快速和髒驗證有更簡單的規則)並不罕見。 – 2011-03-31 20:49:41