問題:到處都是 Null 檢查#
在許多應用程式中,程式碼會不斷地出現 null 檢查。例如,從資料庫中取得員工時,必須先確認回傳值不為 null 才能操作:
Employee e = DB.GetEmployee("Bob");
if (e != null && e.IsTimeToPay(today))
e.Pay();這種程式碼又醜又容易出錯。如果任何地方忘了檢查 null,就會在執行時引發 NullReferenceException。而且這種檢查散佈在程式碼的每個角落,增加了閱讀和維護的困難。
Null Object 模式#
結構#
Null Object 模式的解法是提供一個代表「什麼都不做」的物件來取代 null。以員工系統為例:

Figure 25.1: NULL OBJECT
Employee變成帶有IsTimeToPay()、Pay()等方法的抽象類別(或介面)EmployeeImplementation是實際的員工實作NullEmployee是空物件實作——所有方法都不做任何事:IsTimeToPay()回傳false,Pay()什麼都不做
實作方式#
NullEmployee 被設計為 Employee 內部的私有巢狀類別(private nested class),外部無法直接存取。Employee 類別提供一個靜態唯讀欄位 NULL:
public abstract class Employee
{
public static readonly Employee NULL = new NullEmployee();
public abstract bool IsTimeToPay(DateTime date);
public abstract void Pay();
private class NullEmployee : Employee
{
public override bool IsTimeToPay(DateTime date)
{
return false;
}
public override void Pay()
{
}
}
}使用方式#
DB.GetEmployee() 永遠回傳一個 Employee 實例——找不到時回傳 Employee.NULL,不再回傳 null:
Employee e = DB.GetEmployee("Bob");
e.Pay(); // 安全,不需要 null 檢查如果真的需要區分是否為空物件,可以透過比較靜態欄位來判斷:
if (e == Employee.NULL)
{
// 處理找不到員工的情況
}因為 Employee.NULL 是唯一的 NullEmployee 實例(static readonly),這個比較是可靠的。
重點: Null Object 模式的關鍵好處是消除散佈在整個程式碼中的 null 檢查。呼叫端不需要關心取得的物件是「真正的」員工還是空物件——它只管呼叫方法,空物件會以「什麼都不做」的方式安全回應。這讓程式碼更乾淨、更簡潔,也更不容易出錯。
何時使用 Null Object#
- 當系統中有大量的 null 檢查散佈各處,而 null 代表的語意是「什麼都不做」時
- 當可以定義一個明確的「空行為」(do-nothing behavior)時
- 將 Null Object 設計為私有巢狀類別是常見的做法,這確保了只有一個 null 實例存在,且外部無法任意建立
技巧: Null Object 模式是一個簡單但非常實用的模式。它經常與其他模式搭配使用——例如在後續的薪資系統案例中,
Employee類別的Affiliation預設值就使用了NoAffiliation(一種 Null Object),避免在計算扣款時需要檢查員工是否有加入工會。