目次
サンプルコードを用いてNullオブジェクトの説明とそれを使ったデザインパターンについて解説します。
Nullオブジェクトがなぜ必要なのか?
Nullオブジェクトとはnullに代用されるクラスのことで、オブジェクトが存在しないことを表すオブジェクトのことです。
オブジェクト指向言語にはnullという無効な値を表す値が存在し、値がnullであるオブジェクトのメソッドを呼び出すと、NullReferenceException例外が発生してしまうため、下記のような対策を施すのが一般的です。
if (x == null){
// null例外処理
return;
}
しかし、この対策だとNull例外処理を大量に作る必要があり、書き忘れる懸念もあるため、nullの代わりにNullオブジェクトというクラスを使ってコードを削減しよう!というのがNullオブジェクトパターンです。
サンプルコード
例えばImageModelというオブジェクトが存在するとします。
public class ImageModel
{
public string FilePath { get; }
public TaskModel(string filePath)
{
FilePath = filePath ?? throw new ArgumentNullException(nameof(text));
}
}
上記のクラスを継承して値を固定値にすることでNullオブジェクトを作成できます。また、Nullオブジェクトは1つしか存在しないようにするため、コンストラクターはprivateで実装し、クラス内で静的クラスでインスタンスします。下記コードはその作成例です。
public class NullImageModel : ImageModel
{
public static NullImageModel Instance { get; } = new NullImageModel();
private NullTaskModel() : base("NoImage.png")
{
}
}
設計と実装例
画像の単体表示画面と一覧表示画面を開発する例で説明します。画像データが無ければ”NoImage.png”を表示するという仕様で、Nullオブジェクトパターンでリファクタリングするというシナリオで説明します。
この設計をUML図に書き起こした図を下記に示します。RepositoryはGetByIdメソッドで該当するデータがない場合はnullを返す処理だった場合、単体表示用と一覧表示用のViewModelの2か所にNullを受け取った場合の処理を記述することになってしまいます。
これをNullオブジェクトパターンでRepositoryクラスをリファクタリングします。Repositorクラスでは画像ファイルをサーバからダウンロードしてローカルに保存するといったインフラ層から画像を取得する処理で何かしらのエラーが発生した場合はすべてNulImageModelを返します。
さいごに
面倒くさいNull処理を1か所に固めることができました。NullObjectパターンはStrategyパターンと併用して使うことが多いので、今度はこれについての解説もしてみたいと思います。