Null Object Pattern

前言:

假如在系統中null散佈在有許多地方且null有相對應的邏輯或行為.這時候就很適合使用NullObject Pattern來解決,已Null Object取代null邏輯.

Null可能引申出來問題

我們知道在.Net或Java中大部分都是參考類型,而null是參考類型的預設值,我們來看看以下程式.

1
2
Person p = null;
Console.WriteLine(p.Age);

如果物件p指向null且取得p.Age時就會throw NullReferenceException,所以我們在使用一些參考類型物件前都會先判斷此物件是否為null,在執行後續邏輯.

在系統中某一兩個地方這樣判斷還好,但如果一直重複這樣的判斷會造成程式碼不必要的膨脹….

相較於「不帶有null邏輯」的程式碼,面對null邏輯往往需要花費更多心力.

範例程式

下面有段程式碼在calculate方法中會判斷CartModel物件是否為null並執行相對應邏輯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class PaymentServiceNormal
{
public decimal calculate(CartModel model)
{
decimal result = 0m;
if (model == null)
return result;

result = model.Items.Sum(x => x.Price);

if (result > 400m)
result *= 0.8m;

return result;
}
}

我們可以將calculate方法提取出一個介面並對於null部份提取成一個類別實現此介面

能看到NullPayment這個類別已經被賦予相對應動作操作.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface IPaymentService
{
decimal calculate(CartModel model);
}

public class PaymentService : IPaymentService
{
public decimal calculate(CartModel model)
{
decimal result = model.Items.Sum(x => x.Price);

if (result > 400m)
result *= 0.8m;

return result;
}
}

public class NullPayment : IPaymentService
{
public decimal calculate(CartModel model)
{
return 0m;
}
}

在使用時我們就可統一判斷是否為null來給予相對應物件

這邊有點像是策略者模式(Strategy pattern),判斷要使用哪個邏輯,邏輯統一封裝到類別中.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Program
{
static void Main(string[] args)
{
CartModel model = null;
Console.WriteLine(Calculate(model));
Console.ReadKey();
}

static decimal Calculate(CartModel model)
{
var paymentService = model == null
? (IPaymentService)
new NullPayment()
: new PaymentService();
return paymentService.calculate(model);
}
}

NullObject Pattern缺點:

如果團隊工程師不知道目前程式碼已經存在NullObject實作,會寫出多餘的null測試.
如果目前系統只是需要少量對於null做判斷,這時導入NullObject會導致程式碼變得複雜.

小結:

假如系統中有許多地方需要判斷null並處理相對應的動作就很適合使用NullObject Pattern,但如果判斷null地方不是很多還是判斷就好了

程式碼範例

此文作者:Daniel Shih(石頭)
此文地址https://isdaniel.github.io/nullobjectpattern/
版權聲明:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!


如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^