2010-10-17 36 views
5

最近我遇到了Null Object設計模式,我的同事們說它可以用來消除在整個代碼中遇到的空指針檢查。空對象模式以避免空檢查?

例如,假設DAO類返回有關Customer的信息(在名爲CustomerVO的值對象中)。我的主要課程應該提取firstName和emailId並將電子郵件發送給客戶。

... 
CustomerVO custVO = CustomerDAO.getCustomer(customerID); 
if(custVO != null) { // imp, otherwise we may get null ptr exception in next line 
    sendEmail(custVO.getFirstName(), custVO.getEmailID()); 
} 
... 

這是很簡單的例子,但這種null檢查可以基於價值對象的複雜性在你的代碼迅速蔓延。

我有一個空檢查兩個問題, - 他們往往tmake代碼醜陋不堪,閱讀 - 較少經驗的開發人員把不必要的null檢查的時候,其實他們應該拋出異常。例如,在上面的代碼中,最好從getCustomer()本身拋出異常,因爲如果它無法找到給定CustID的客戶信息,則表示CustID無效。

好吧,回到空對象模式,我們可以使用'null'CustomerVO對象來隱藏空檢查嗎?

CustomerVO { 
    String firstName = ""; 
    String emailID = ""; 
} 

難道這不合理嗎?你怎麼看?

爲了儘量減少應用程序中的空檢查,您遵循的是什麼?

回答

2

在這種情況下,空對象可能不合適,因爲默認實際上可能隱藏實際上是異常的東西。如果您發現自己不得不檢查是否有安全的null來執行一些其他活動,則null模式不會爲您購買任何東西。

正如你所陳述的,許多新開發者花費時間試圖保護他們的代碼免受異常情況的影響,這些異常情況會導致程序停止。

3

雖然空對象模式有它的用處,但您仍然需要在這裏進行檢查,否則您將嘗試sendEmail()到一個空字符串的電子郵件地址(或者您將支票放入sendEmail(),它可以很容易地檢查null)。

如果CustomerVO類實現了sendEmail()方法,那麼空對象模式在這裏真正有用。那麼你可以簡單地鏈上的電話一起,因爲getCustomer()合同將確保null參考不予退還:

CustomerDAO.getCustomer(customerID).sendEmail(); 

在這種情況下,sendEmail()方法將檢查其被要求採取行動的特殊'空對象',並且什麼都不做(或者什麼都適合)。

+1

+1,但我不希望我的客戶擁有sendEmail方法。這只是給顧客分類太多的責任。這就像給每個班級一個「打印」方法,現在不得不在每個班級實施打印邏輯,或者讓每個班級依賴某個打印管理器,而它可能僅限於打印管理器和實際啓動打印的表單/類,以便打印管理器要打印的類。在空模式非常好的情況下,在單元測試中,例如提供一個不做任何事情的日誌類,因此您不必修改正在測試的代碼。 – 2010-10-17 07:50:26

1

如果你的getCustomer方法拋出一個異常,如果沒有找到客戶,而不是返回null?

答案當然是,它取決於:是否應該幾乎從不是客戶ID不存在的情況?換句話說,這是一種特殊的情況嗎?如果是這樣,一個例外是適當的。

但是,在數據訪問層中,某些不存在的東西通常很正常。在這種情況下,最好不要拋出,因爲它不是一個意外的例外情況。

'返回一個具有空字段的非空對象'可能不會更好。你怎麼知道如果沒有添加一些可能比空檢查更差的檢查代碼,返回的對象是否'有效'?

所以,如果它可能是一個正常的狀態,被取回的東西不存在,空檢查模式可能是最好的。如果它是意想不到的,那麼讓數據訪問方法拋出一個NotFound異常可能會更好。

+0

看到我的答案,如果某件事不能存在,請有一個方法來檢查它是否存在。這樣,您只需要在例外的情況下拋出異常。 – nicodemus13 2012-05-15 15:55:28

1

我有這種類型的代碼的問題,這是一種常見的模式。如果你分解你實際做的事情,你根本不需要空檢查。在我看來,這裏的問題是您違反了SRP。

方法CustomerVO custVO = CustomerDAO.getCustomer(customerID);做了兩件事。

首先,如果客戶存在,則返回客戶,如果沒有客戶,則返回null。這是兩種截然不同的操作,應該這樣編碼。

更好的方法是:

bool customerExists = CustomerDAO.exists(customerID); 

if (customerExists) 
{ 
    CustomerVO custVO = CustomerDAO.getCustomer(customerID); 
    sendEmail(custVO.getFirstName(), custVO.getEmailID()); 
} 
else 
{ 
    // Do whatever is appropriate if there is no such customer. 
} 

}

所以,分裂方法分爲二,一個用於檢查所請求的對象是否存在和其實際檢索的第二個。不需要任何例外,設計和語義非常清晰(在我看來,它具有不存在的返回null模式。此外,在此方法中,如果請求的客戶不存在,則CustomerDAO.getCustomer(customerID)方法將拋出ArgumentException。畢竟,你已經要求一個Customer,並沒有一個。

另外,在我看來,沒有方法應該返回nullNull是明確的,'我不知道什麼是正確答案,我沒有價值返回'。 Null不是一個意思,它是一個缺少的意思。問問自己,'爲什麼我要回到我認識不應該發生的事情?你的方法GetCustomer顯然應該返回一個Customer。如果您返回null,那麼您只是將責任推到支持呼叫鏈和違約的責任上。如果有意義的默認值,請使用它,但在此引發異常可能會讓您覺得設計的正確性有點困難。

0

該名稱是NULL對象設計模式而不是NULL檢查設計模式。空對象表示沒有對象。當你在合作中有對象時,應該使用它。在你的情況下,你正在檢查NULL檢查應該罰款的對象的存在。

NULL設計模式並不意味着替換NULL異常處理。這是NULL設計模式的副作用之一,但意圖是提供默認行爲。

NULL檢查不應該替換爲NULL設計模式對象,因爲它可能導致應用程序中的安全缺陷。

請看下面的文章,詳細介紹DO的設計模式和Donet的設計模式。

http://www.codeproject.com/Articles/1042674/NULL-Object-Design-Pattern