第4章 逆引きカタログ その他
Authors:Agata Toshitaka
Nullオブジェクトという言葉を聞いて何を想像しますか? Nullという言葉から想像すると、何もできないオブジェクトというイメージが浮かびます。
そう感じられた方はビンゴです!
Nullオブジェクトは、nullの代わりに”何の処理も行わない”オブジェクト(= Nullオブジェクト)を使用するパターンです。
Javaではnullはデリケートな存在です。
nullが設定された変数のメソッドを呼び出すと、簡単にNullPointerExceptionが発生してしまいます。
このため、オブジェクトがnullかそうでないかということを常に意識してコードを書く必要が出てきます。
通常はif文でオブジェクトがnullかどうかを逐一チェックをして、NullPointerExceptionの発生を防ぐことになります。
しかしこの方法ですと、オブジェクトがnullになる可能性のある箇所すべてにnullチェックが入ってしまいます。
チェック箇所が多くなってくると、if文だらけになってコードもわかりにくくなってきます。
この状態がnullチェック地獄です。
Nullオブジェクトパターンをうまく適用すると、nullチェックのif文を一掃できます。
Nullオブジェクトパターンではオブジェクトがnullである場合に、nullの代わりに”何の処理も行わない”Nullオブジェクトを使います。
これによりオブジェクトがnullである可能性がなくなり、nullチェックが不要になります。
Nullオブジェクトパターンを使うとオブジェクトがnullであるかどうかを気にすることなく、nullチェックなしで安心してオブジェクトが使用できるようになります(図6〜7)。
それではより具体的な例を見てみましょう。サンプルは商品の割引価格を計算するコードです。
割引価格には「会員の割引価格」「セール時の割引価格」「通常の割引価格」の3種類があります。
割引価格の種類を決定するのは、Mainクラス実行時のアプリケーション引数です。
Mainクラスは「java Main 価格 割引ロジック名(sale, member, 指定しないのいずれか)」の形式で実行します。
例えば「java Main 1000 member」で実行すると会員割引価格となり、「java Main 1000 sale」で実行するとセール時の割引価格となります。
「java Main 1000」と指定がない場合は、通常の割引価格になります。
各クラスの役割は次のようになります(図8〜9)。
◎DiscountLogic(リスト14)
割引計算のロジックのインタフェースです。各割引計算クラスは、
リスト14-@のcalcDiscountPrice()メソッド
を実装して作成します。
リスト14 DiscountLogic.java
public interface DiscountLogic {
int calcDiscountPrice(int price); …@
}
◎MemberDiscountLogic(リスト15)
会員割引の価格の計算ロジッククラスです。会員割引では、1,000円以上の場合5%OFFになります。
1,000円未満では割引はありません。
リスト15 MemberDiscountLogic.java
public class MemberDiscountLogic implements DiscountLogic {
public int calcDiscountPrice(int price) {
if(price >= 1000) {
return (int) (price * 0.95);
} else {
return price;
}
}
}
◎SaleDiscountLogic(リスト16)
セール時の割引価格の計算ロジッククラスです。一律5%OFFとなります。
リスト16 SaleDiscountLogic.java
public class SaleDiscountLogic implements DiscountLogic {
public int calcDiscountPrice(int price) {
return (int) (price * 0.8);
}
}
◎NullDiscountLogic(リスト17)
通常価格の計算ロジッククラスで、Nullオブジェクトの役割を果たします。
リスト17-@のように処理は何も行わず、受け取った価格をそのまま返却しています。
リスト17 NullDiscountLogic.java
public class NullDiscountLogic implements DiscountLogic {
public int calcDiscountPrice(int price) {
return price; …@
}
}
メイン処理です。
アプリケーション引数から適切な割引計算ロジックを
リスト18-@のcreateDiscountLogic()メソッドで生成して、割引価格を計算しコンソールに出力します。
リスト18-Bでは、割引計算の種類が指定されていない場合でも必ずNullDiscountLogicオブジェクトが返却されます。
DiscountLogicがnullになることはありませんので、
リスト18-AでDiscountLogicのメソッドを呼び出すときもnullチェックは不要になります。
リスト18 Main.java
public class Main {
public static void main(String[] args) {
int price = Integer.parseInt(args[0]);
String type = args.length > 1 ? args[1] : null;
DiscountLogic logic = createDiscountLogic(type); …@
int discountPrice = logic.calcDisCountPrice(price); …A
System.out.println("割引前の価格=" + price);
System.out.println("割引後の価格=" + discountPrice);
System.out.println(
"使用した割引ロジッククラス=" + logic.getClass().getName());
}
private static DiscountLogic createDiscountLogic(String type) {
if ("sale".equals(type)) {
return new SaleDiscountLogic();
} else if ("member".equals(type)) {
return new MemberDiscountLogic();
| B |
} else {
return new NullDiscountLogic();
}
|
}
}
◎Nullオブジェクトのメリット
Nullオブジェクトでは何も処理を行わないクラスを利用することでnullチェックをなくします。
nullチェックのためのif文が不要になることで、コードが簡潔に記述できるようになります。
nullチェック漏れによる不測のエラーもなくなります。
◎ストラテジパターンとの組み合わせ
Nullオブジェクトは
ストラテジパターンと組み合わせて利用されることが多いです。
「具体的な例」でも割引計算ロジックを切り替えるために、共通のインタフェースDiscountLogicを定義していますので、ストラテジパターンと似たパターンであるといえます。
◎シングルトンパターンとの組み合わせ
Nullオブジェクトは
シングルトンパターンとして実装されることもあります。
Nullオブジェクトではインスタンスごとに振る舞いが変化しないので、インスタンスは全体で1個あれば十分だからです。
「
具体的な例」のNullオブジェクトをシングルトンパターンに書き換えると
リスト19、
リスト20のようになります。
リスト19 NullDiscountLogic.java(シングルトンパターン版)
public class NullDiscountLogic implements DiscountLogic {
| シングルトン化のために追加した部分 |
private static NullDiscountLogic instance = new NullDiscountLogic();
private NullDiscountLogic() {
}
public static NullDiscountLogic getInstance() {
return instance;
}
|
public int calcDiscountPrice(int price) {
return price;
}
}
リスト20 Main.java(シングルトンパターン版)
public class Main {
(中略)
private static DiscountLogic createDiscountLogic(String type) {
if ("sale".equals(type)) {
return new SaleDiscountLogic();
} else if ("member".equals(type)) {
return new MemberDiscountLogic();
} else {
| 常に同じインスタンスが使用される |
return NullDiscountLogic.getInstance();
|
}
}
}