Java.use(better);
■ NullObject パターンの応用
二分木を利用して式を評価する事例で話を進めます。ここでは、定数と二項式だけを対象とします。たとえば、式 (3+4)-2 を表わす二分木は、次のようになります。
Expr expression = new Expr("-", new Expr("+", new Expr("3"), new Expr("4")), new Expr("2")); System.out.println(expression.eval());
これを実行すると、次の結果が得られます。
5
この結果から、式の値が 5 になるのが分ります。
これは、次のように実現します。
class Expr { private String value; private Expr left; private Expr right; Expr(String value, Expr left, Expr right) { this.value = value; this.left = left; this.right = right; } Expr(String value) { this(value, null, null); } int eval() { int op1 = (left == null) ? 0 : left.eval(); int op2 = (right == null) ? 0 : right.eval(); if (value.equals("+")) return op1 + op2; if (value.equals("-")) return op1 - op2; return Integer.valueOf(value); } }
ここでも、NullPointerException が発生しないように、条件演算子 ?: に伴う条件式が必要です。そのため、コードが複雑で見通しも悪くなるだけでなく、バグの温床になりがちです。
この問題を解決するために「NullObject パターン」のアプローチを発展させます。今度は、
Expr expression = new Expr("-", new Expr("+", new Const("3"), new Const("4")), new Const("2"));
Expr に代えて、定数に特化した Const を利用します。すると、
int eval() { int op1 = left.eval(); int op2 = right.eval(); if (value.equals("+")) return op1 + op2; if (value.equals("-")) return op1 - op2; return 0; }
(if 文は残るものの)条件演算子に伴う条件式が不要になるので、コードが簡潔で見通しも良くなります。そのためには、
class Expr {Expr(String value) { this(value, null, null); }}
引数を1つだけ持つコンストラクター Expr(String) に代えて、
class Const extends Expr { Const(String value) { super(value, null, null); } int eval() { return Integer.valueOf(value); } }
定数に特化したクラス Const を用意します。
たとえば、NullObject パターンを適用して、特化したクラス NullNode を「0」と見なすなら、定数を表わすために特化したクラス Const は「1」と見なせます。すると「2」に当たるクラスがあってもいいはずです。今度は、
Expr expression = new Sub( new Add(new Const(3), new Const(4)), new Const(2));
Expr に代えて、加算/減算に特化した Add/Sub を利用します。そこでは、
abstract class Expr { protected int value; protected Expr left; protected Expr right; Expr(int value, Expr left, Expr right) { this.value = value; this.left = left; this.right = right; } abstract int eval(); }
Expr を具象クラスから抽象クラスに変更して、eval も具象メソッドから抽象メソッドに変更します。すると、
class Const extends Expr { Const(int value) { super(value, null, null); } int eval() { return value; } } class Add extends Expr { Add(Expr left, Expr right) { super(0, left, right); } int eval() { return left.eval() + right.eval(); } } class Sub extends Expr { Sub(Expr left, Expr right) { super(0, left, right); } int eval() { return left.eval() - right.eval(); } }
すべての if 文や条件演算子に伴う条件式が不要になるので、コードが簡潔で見通しも良くなります。
↑ TOP
》作業中です《