あるオブジェクトが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つだけ作るようにしておけばよいだろう。