Null Objectパターンについて勉強したい

はいどーもこんにちは、Nullよりもヌルヌルの女の子が好きなエロゲー制作者(予定は未定)のツミオです。
本日はNull Objectパターンについて勉強したいと思いますよ。

Null Objectパターンとは

C#を使っていて苛つくことと言えば、やはりNull参照エラーではないでしょうか。
if文でnullかどうかをチェックするのは手間ですし、Null条件演算子を使ってもNull条件演算子を使うこと自体を忘れちゃってNull参照エラーが出ちゃう場合もあります。
「これはどげんかせんといかん」
というわけで生まれたのがNull Objectパターンのようです(たぶん)。
Null Objectパターンを使うと、もう面倒なnullチェックは不要になります。
nullを表すオブジェクトを用いて、nullのオブジェクトもnullでないオブジェクトも同じように扱うことができるのです!
「AというオブジェクトとBというオブジェクトを同じように扱う」でピンと来た方もいるかなと思いますが、そうです。
Null Objectパターンでもインターフェイスを活用しますよ。

具体例

現在あなたはRPGの図鑑を作ろうとしているとします。
ここでは話を簡単にするため、図鑑には以下のデータのみが含まれていることとします。

・アイテムの名前
・アイテムの説明
・アイテムの画像パス

その図鑑にはとりあえず仮のデータを入れておきたいものがいくつかあって、それをnullで表したいとします。
しかしそのままNullを使うと当然のことながら、アイテムの名前を参照しようとしたときにNull参照エラーが発生します。
図鑑クラスを使う側でnullチェックをしてもよいのですが、図鑑クラスを使うクラスが多くなればなるほど、Nullチェックに漏れが出る蓋然性が高くなります。
そこでNull参照オブジェクトを使うわけです!
というわけでコードをドン(サンプルなので、ただのコンソールアプリケーションです)。

Mainメソッドはこんな感じで書いてみてください。

コードの解説

GameBookクラスが図鑑の本体です。
このクラスはIGameBookItem型のリストを保持しています。
Addメソッドを見てください。
Addの中でnullチェックをしていますね(null合体演算子)。
もしも追加されるアイテムがnullだったならば、NullGameBookItem型のインスタンスをリストに追加しています(こいつがNull Object)。
GetByNameはその名の通り、図鑑に登録されたアイテムの名前を検索し、一致したアイテムを返すメソッドです。
検索した結果なにも一致するものがなければNullObjectを返しています。
最後のGetEnumeratorは、このクラスをforeachで回せるようにするためだけに書いたものです(_listフィールドの内容を返してるだけ)。深い意味はないです。

IGameBookItemインターフェイスはGameBookItemクラスとNullGameBookItemクラスで実装されています。
このインターフェイスを実装したクラスは、NameプロパティとDescriptionプロパティとShowPictureメソッドを実装しなければなりません。
両方のクラスで実装されていますね。
ただしNullGameBookItemは決め打ちで「Nullであることを表す」文字列やら何やらを設定しています(Null Objectです)。

さてMainメソッドを見てください。
ここでGameBook型のインスタンスが作成されています。
このインスタンスのAddメソッドを使って、スライムやらゴブリンやらnullやらが登録されています。
foreachで回してみると、以下のような結果になります。

最初の行ですが、NullGameBookItemを登録しているのでUnknownと表示されるのは当然ですね。
さらに次のスライムも想定通りの結果です。
では次のnullはどうでしょうか?
もしもNull Objectパターンを使っておらず、本当に単なるnullが登録されたなら、foreach内のitem.Nameやitem.DescriptionでNull参照エラーが発生していたはずです!
ですが今回はNull Objectパターンを使っているため、nullが登録されたならNullGameBookItem型を登録するようにしています。
なので、通常の図鑑アイテム(GameBookItem)とNullの図鑑アイテム(NullGameBookItem)はIGameBookItemインターフェイスを通じ、全く同じように扱う事ができるのです!
GameBook型を使う側は、実行時に図鑑の中身がNullであるかどうかを考慮する必要も全くありません!
こ、これは便利だ!

あとはGetByNameメソッドで「存在しないアイテム」を検索したときにもきちんとNull Objectが返されていることを確認してください(ドラゴン)。
こちらでもやはり、返された値がnullであるかどうかを考慮する必要がありません。

いやあこれはいいパターンです。
僕も今度からどんどん使っていこうと思います。

終わりに

『Adaptive Code』を読んでいて「おっ、これは!」と思ったパターンを発見したので記事にしてみました。
Null Objectというのは名前だけは聞いて知っていたのですが、実際にどのように実装するのかまでは勉強していませんでした。
今回はいい機会だったので簡単に作ってみました。
これは本当にいいパターンだと思ったので、これから活用していきたいですね。

ほなそんな感じでまた。

フォローする

『Null Objectパターンについて勉強したい』へのコメント

  1. 名前:通りすがり 投稿日:2018/05/09(水) 16:35:16 ID:d2eb5cb80

    “しかしそのままNullを使うと当然のことながら、アイテムの名前を参照しようとしたときにNull参照エラーが発生します。” じゃなくて、そもそも意図しない場所にnullが存在すること自体が問題なので、これは事実上の”例外の握り潰し”になってしまうのでは?

    • 名前:ツミオ 投稿日:2018/05/10(木) 08:44:58 ID:3d1b1688e

      例えば電卓アプリを自作したとして、何も対策しないとゼロ除算で例外エラーが出ますが、ユーザーはゼロ除算する可能性があります。
      そこでif文を使ってバリデーションを実装するのは一般的だと思います(想定されるエラーに対してtry-catchは使わない)。
      これもそれと同じで、nullが存在するのは想定の範囲内(「その図鑑にはとりあえず仮のデータを入れておきたいものがいくつかあって、それをnullで表したい」とある通り)で、nullは「存在しないアイテム」として表現したいというシチュエーションなので問題ないんじゃないでしょーか(if文で場合わけするかNull Objectパターンを使うかの違い)。