興味の源泉

自分が興味を起こせるようなネタを雑多に書き綴るブログ

【C#】Null Object パターンでヌル退治

f:id:nanami_yamato:20190418031632p:plain

Null Object パターンでNullチェックを無くす

他方もすなるNull Object パターンといふものを、我もしてみむとてするなり(土佐日記風)

ということで、今回はNullObjectパターンと呼ばれるものを使ってみようかと思います。

簡単に言うと、「nullだったらこういう処理をする」みたいな分岐を消すための方法です。

なぜ消したいかというのは以下のようなnullチェックが増えるとどんどんネストが深くなっていって見づらくなっていくからです。
以下のように。

string name;
if (testClass == null)
{
    name = "No Name";
}
else
{
    var person = testClass.GetPerson();
    if (person == null)
    {
        name = "No Name";
    }
    else
    {
        name = person.GetName();
    }
}


ちょっと大げさに書きましたが、継ぎ足し継ぎ足しやってるコードだと時々見かけます。


ではNullObjectパターンがどんな感じなのか書いてみます。

今回はシンプルに生成時に値を設定しその値を取得するクラスを作ります。


まずは抽象クラスでベースクラスを作ります。

public abstract class NullObjectSampleBase
{
    protected int value = 0;

    public virtual bool IsNull()
    {
        return false;
    }

    public abstract int GetValue();
}

nullのような無効なクラスかを取得できるIsNull関数を作ります。
特に関数をoverrideしなくても良いようにfalseを返すようにします。

抽象メソッドとして値を取得するGetValue関数を定義します。


次にnullにあたるオブジェクト(以下NullObjectと呼びます)のクラスを継承して作成します。

public class SampleNullObject : NullObjectSampleBase
{
    public override bool IsNull()
    {
        return true;
    }

    public override int GetValue()
    {
        return -1;
    }
}

IsNull関数はtrueを返すようにして、このオブジェクトはNullObjectなんだよとわかるようにします。

GetValue関数でNullObjectの処理を書きます。
今回はNullObjectの値は-1にします。


継承して値を設定し取得するSampleValueクラスを作ります。
もう一例としてSampleValue2 クラスも作ります。
SampleValue2 クラスはGetValueで設定した値を2倍して返します。

public class SampleValue : NullObjectSampleBase
{
    public SampleValue(int setValue)
    {
        value = setValue;
    }

    public override int GetValue()
    {
        return value;
    }
}

public class SampleValue2 : NullObjectSampleBase
{
    public SampleValue2(int setValue)
    {
        value = setValue;
    }

    public override int GetValue()
    {
        return value * 2;
    }
}


それではこれらを使ってテストコードを書いてみます。

NullObjectとSampleValue1とSampleValue2をそれぞれ1つずつ作り、設定した値を取得してログに出力します。

public void NullObjectPatternTestSimplePasses()
{
    List<NullObjectSampleBase> valList = new List<NullObjectSampleBase>();

    NullObjectSampleBase valNull = new SampleNullObject();
    valList.Add(valNull);

    NullObjectSampleBase val1 = new SampleValue(100);
    valList.Add(val1);

    NullObjectSampleBase val2 = new SampleValue2(100);
    valList.Add(val2);

    for (int i = 0; i < valList.Count; ++i)
    {
        Debug.Log(valList[i].GetValue());
    }
}


結果は以下のようになります。

-1
100
200


このパターンで最大のメリットはNullObjectを含めて同じ関数を呼ぶことができる点です。

無効を意味するオブジェクトを作ってそのオブジェクトでは処理を行わない考え方はかなり有用なので積極的に使っていきたいです。