2009-05-19 48 views
3

假設您有一個可以完成某種工作的子系統。它可能是任何東西。顯然,在這個子系統的入口點上,輸入會有一定的限制。假設這個子系統主要由GUI來調用。子系統需要檢查它收到的所有輸入以確保它是有效的。如果輸入無效,我們不想FireTheMissles()。 UI也對驗證感興趣,因爲它需要報告出錯的地方。也許用戶忘了指定一個目標或者在啓動板本身瞄準導彈。當然,你可以返回一個空值或者拋出一個異常,但是這並不能告訴用戶什麼地方出錯了(當然,除非你爲每個錯誤編寫一個單獨的異常類,如果if這是最佳做法)。在輸入驗證中避免重複的代碼

當然,即使有例外,你也有問題。用戶可能想知道輸入是否有效,然後單擊「Fire Missles!」按鈕。你可以編寫一個單獨的驗證函數(當然IsValid()並沒有真正的幫助很多,因爲它不會告訴你什麼出了問題),但是你會從按鈕點擊處理函數中調用它,然後再從FireTheMissles中調用它( )函數(我真的不知道這是如何從一個模糊的子系統變成一個引火計劃)。當然,這並不是世界的盡頭,但似乎很愚蠢的是連續調用兩次相同的驗證函數而沒有任何改變,特別是如果這個驗證函數需要計算1GB文件的散列值。

如果函數的前提條件是清晰的,GUI可以進行自己的輸入驗證,但是我們只是複製了輸入驗證邏輯,而其中的更改可能不會反映在另一箇中。當然,我們可以在GUI上添加一張支票以確保導彈目標不在盟國內,但如果我們忘記將它複製到FireTheMissles()例程中,那麼當我們切換到時會意外炸燬我們的盟友一個控制檯界面。

因此,簡而言之,你如何實現以下目標:

  1. 輸入驗證,告訴你不只是出事了,但具體是什麼地方出了錯。
  2. 能夠在不調用依賴它的函數的情況下運行此輸入驗證。
  3. 沒有雙重驗證。
  4. 沒有重複的代碼。

另外,我只是想到了這一點,但錯誤消息不應該寫在FireTheMissles()方法中。 GUI負責選擇適當的錯誤消息,而不是GUI調用的代碼。

回答

3

「的子系統需要檢查所有收到以確保它是有效的投入」的投入沒有那麼多的參數列表

想,但作爲一個消息,它會之後更容易。

消息類有一個IsValid成員函數,它會記住IsValid是否被調用以及結果是什麼。它還會記住它的狀態,如果狀態發生變化,那麼它需要重新驗證。這個消息類還保留了一個驗證錯誤列表。

現在,UI會生成一個TargetMissiles消息,UI可以驗證它,或直接將其傳遞給MissileFiring子系統,它會檢查消息是否已驗證,如果驗證不成功,則繼續/失敗,取決於。 用戶界面獲取消息,驗證列表已填充。

帶有驗證的消息位於單獨的庫中。沒有代碼是重複的。

This sound OK?

2

這就是Model-View-Controller的全部內容。

您建立了一個模型(由座標,導彈類型和導彈數組成的發射),並且該模型具有一個返回錯誤/警告列表的驗證方法。當您更新模型時(在鍵入時,按下按鈕),您調用驗證方法並向用戶顯示任何警告,錯誤等。(Eclipse在對話框中的工具欄下方有一個小區域這樣做,你可能想看看。)

當模型是有效的,你激活發射導彈按鈕,以便用戶知道他們可以發射導彈。如果您有特別頻繁地調用的更新事件或特別昂貴的驗證的一部分,那麼您可以在模型上使用validate_light方法來驗證僅容易執行的部分。

當您切換到基於控制檯的UI時,可以從命令行參數構建模型,調用相同的驗證方法(並將錯誤報告給stderr),然後啓動導彈。

+0

MVC比輸入驗證要多得多。當它到達輸入驗證本身時,你所建議的與Binary Worrier提出的有什麼不同?如果是這樣,怎麼樣?如果沒有,那麼爲了實現這種錯誤處理方法,是否真的有必要做MVC? (並不是說最終的應用程序肯定不會是MVC,但我甚至沒有考慮過GUI,除非知道它將需要從我的導彈發射子系統中得到什麼。) – 2009-05-19 13:07:43

+0

+1簡而言之:模型定義它的任何部分是有效的,任何人都必須確保他們聽從這些限制。 – 2009-05-19 14:20:22

0

我會建議一個輸入驗證類,它在其構造函數中接受輸入類型(一個枚舉),並提供一個公有的IsValid方法。

IsValid方法應該返回一個布爾值TRUE爲有效,而爲無效返回FALSE。它還應該有一個OUT參數,它接受一個字符串並將狀態消息分配給該字符串。調用者可以自由地忽略該消息,如果它適合上下文,或者將其報告給GUI。

因此,在僞代碼(原諒德爾福的語法,但它應該是可讀的任何人):

//different types of data we might want to validate 
TValidationType = (vtMissileLaunchCodes, vtFirstName, 
    vtLastName, vtSSN); 

TInputValidator = class 
public 
    //call the constructor with the validation type 
    constructor Create(ValidationType: TValidationType); 

    //this should probably be ABSTRACT, implemented by descendants 
    //if you took that approach, then you'd have 1 descendant class 
    //for each validation type, instead of an enumeration 
    function IsValid(InputData: string; var msg: string): boolean; 

然後使用它,你會做這樣的事情:

procedure ValidateForm; 
var 
    validator: TInputValidator; 
begin 
    validator := TInputValidator.Create(vtSSN); 
    if validator.IsValid(edtSSN.Text,labelErrorMsg.Text) then 
    SaveData; //it's valid, so save it! 
    //if it wasn't valid, then the error msg is in the GUI in "labelErrorMsg". 
end; 
0

每條數據都有自己的元數據(類型,格式,單位,掩碼,範圍等)。不幸的是,這並不總是指定。

GUI控件需要檢查包含元數據的輸入,並在數據無效時發出警告/錯誤。

例如:一個數字有一個範圍。範圍由元數據提供,但範圍檢查由控件提供。

1

雙驗證。在許多情況下,驗證是三重的(例如,DB中的FK和非空字段)。根據您的平臺,可能會對驗證規則進行一次編碼。例如,您的前端和後端代碼可以共享C#業務類。或者,您可以將驗證規則存儲爲後端和前端都可以訪問應用程序的元數據。

事實上,你需要不同的響應來驗證問題(例如火災導彈按鈕甚至不應該啓用,直到其他輸入有效),將會有不同的代碼與相同的規則相關聯。