ゲームが作れるようになるまでがんばる日記

ゲーム制作のことを中心にゲームに関することを書いています

ヌルオブジェクトを導入する

あるオブジェクトがnullがどうかのチェックが頻繁に出てくるようなら、ヌルオブジェクトを導入することを考える。
たとえば、次のようなnullチェックをしているコードがあった場合。

public class Player {
    private final Magic magic;
    public Player() {
        magic = null;
    }
    public Player(Magic magic) {
        this.magic = magic;
    }
    public void doSpell() {
        if (magic != null) { // nullチェック
            magic.doSpell();
        }
    }
}
public class Magic {
    private final String name;
    public Magic() {
        name = null;
    }
    public Magic(String name) {
        this.name = name;
    }
    public void doSpell() {
        System.out.println(name);
    }
}
public class Main {
    public static void main(String[] args) {
        Player fighter = new Player();
        Player magician = new Player( new Magic("Fire") );

        fighter.doSpell();
        magician.doSpell();
    }
}

PlayerクラスのdoSpellメソッドが呼ばれたとき、magicフィールドがnullかどうかチェックして、nullでないときのみMagicクラスのdoSpellメソッドを呼ぶようにしている。
これにヌルオブジェクトを導入してnullチェックを減らすことにする。
まず、nullチェックを行っているMagicクラスのサブクラスとしてヌルオブジェクトのクラスを作成する。

public class NullMagic extends Magic {
}

ヌルオブジェクトか判定するisNullメソッドを作る。

public class Magic {
    ...
    public boolean isNull() {
        return false;
    }
}
public class NullMagic extends Data {
    @Override public boolean isNull() {
        return true;
    }
}

@Overrideと書いておくと、正しくオーバーライドされていないときにコンパイラがエラーを出してくれる。
ここでいったんコンパイルして確認。
次にnullをヌルオブジェクトで置き換える。ここではMagicがないという意味のnullだから、Playerクラスのコンストラクタの部分。

    public Player() {
        magic = new NullMagic();
    }

次にnullチェックをisNullメソッドに置き換える。

    public void doSpell() {
        if (!magic.isNull()) {
            magic.doSpell();
        }
    }

ここでコンパイルして確認。
次にisNullメソッドを使った条件判断を削除していく。ここでは、PlayerクラスのdoSpellメソッドの部分。nullのときには何もしないで、nullでないときはMagicのdoSpellメソッドが呼ばれる。そこでヌルオブジェクトNullMagicでdoSpellをオーバーライドして何もしないという処理を書く。

public class NullMagic extends Magic {
    ...
    @Override public void doSpell() {
    }
}

そして、条件判断部分を削除する。

    public void doSpell() {
        magic.doSpell();
    }

で、コンパイルして確認。こうしてヌルオブジェクトを導入することによってnullチェックを削除することができた。
あるクラスのヌルオブジェクトは複数作成する必要はないので、Singletonでプロジェクトに1つだけ作るようにしておけばよいだろう。


参考文献:『Java言語で学ぶリファクタリング入門』