概述
在這個快速教程中,我們將看一下Null物件模式,這是設計模式的一個特例,一個經常被我們遺忘的特例。我們將描述它的目的以及我們何時應該考慮使用它。
像往常一樣,我們還將提供一個簡單的程式碼示例。
空物件模式
在大多數物件導向的程式語言中,我們會考慮不進行null引用,為此我們經常被迫寫空檢查:
Command cmd = getCommand();
if (cmd != null) {
cmd.execute();
}
複製程式碼
有時,如果此類if語句的數量變多,則程式碼的可讀性會變得很差,難以閱讀且容易出錯。這是Null物件模式可以派上用場的時候。
**Null物件模式的目的是最小化這種空檢查。**相反,我們可以識別null行為並將其封裝在客戶端程式碼所期望的類中。這樣我們就不再需要處理空引用的特殊處理。
我們可以簡單地處理null物件,就像處理實際包含一些更復雜的業務邏輯的給定型別的任何其他例項一樣,以此來保持程式碼的清潔。
由於null物件不應具有任何狀態,因此無需多次建立相同的例項。因此,我們經常將null物件實現為單例。
Null Object Pattern的UML圖
讓我們直觀地看一下模式:
我們可以看到,會有以下參與者:
- 客戶端需要AbstractObject的例項
- AbstractObject定義客戶期望的執行邏輯- 它還可以包含實現類的共享邏輯
- RealObject實現了AbstractObject並提供了真實的行為
- NullObject實現AbstractObject並提供
do nothing
行為
案例
現在我們已經清楚地瞭解了這個理論,讓我們看一個例子。
假設一個場景,有一個訊息路由器模組。每條訊息都應分配有效的優先順序。我們的系統應該將高優先順序訊息路由到SMS閘道器,而具有中等優先順序的訊息應該路由到JMS佇列。
但是,有時會出現**“未定義”或空優先順序的訊息**。這些訊息應該從進一步處理中丟棄。
首先,我們將建立路由器介面:
public interface Router {
void route(Message msg);
}
複製程式碼
接下來,讓我們建立上述介面的兩個實現 - 負責路由到SMS閘道器的介面以及將訊息路由到JMS佇列的介面:
public class SmsRouter implements Router {
@Override
public void route(Message msg) {
// implementation details
}
}
複製程式碼
public class JmsRouter implements Router {
@Override
public void route(Message msg) {
// implementation details
}
}
複製程式碼
最後,讓我們實現我們的null物件:
public class NullRouter implements Router {
@Override
public void route(Message msg) {
// do nothing
}
}
複製程式碼
我們現在準備將所有部分組合在一起。讓我們看看示例客戶端程式碼的外觀如何:
public class RoutingHandler {
public void handle(Iterable<Message> messages) {
for (Message msg : messages) {
Router router = RouterFactory.getRouterForMessage(msg);
router.route(msg);
}
}
}
複製程式碼
我們可以看到,無論RouterFactory返回什麼實現,**我們都以相同的方式處理所有Router物件。**這使我們能夠保持程式碼的清潔和可讀性。
何時使用空物件模式
我們應該使用Null物件模式,否則客戶端會檢查null只是為了跳過執行或執行預設操作。 在這種情況下,我們可以將do nothing
邏輯封裝在空物件中,並將其返回給客戶端而不是空值。這樣,如果給定例項為null,則客戶端程式碼不再需要知道 。
這種方法也遵循一般的物件導向原則。
為了更好地理解何時應該使用Null物件模式,讓我們假設我們必須實現CustomerDao介面,定義如下:
public interface CustomerDao {
Collection<Customer> findByNameAndLastname(String name, String lastname);
Customer getById(Long id);
}
複製程式碼
如果沒有客戶匹配提供的搜尋條件,大多數開發人員將從findByNameAndLastname()返回Collections.emptyList()
。這是遵循Null物件模式的一個很好的例子。
相反,getById()
應返回具有給定id的客戶。呼叫此方法的人希望獲得特定的客戶實體。如果不存在這樣的客戶,我們應該顯式返回null
以表示提供的ID存在問題。
與所有其他模式一樣,我們需要在盲目實現Null物件模式之前考慮我們的特定用例。否則,我們可能會無意中在程式碼中引入一些很難找到的錯誤。
結論
在本文中,我們瞭解了Null物件模式是什麼以及何時可以使用它。我們還實現了一個設計模式的簡單示例。