デザインパターンを覚えたい (Interpreter)

Interpreterパターン

構文解析を行うパターン。各文規則に対してクラスを割り当て、逐次でパースすることで実行に持っていく。
ポイントは

  • 文規則のクラスを再帰的に呼び出すことで、構文木を生成する
  • 独自表現が使えるため、プログラムの柔軟性が高まる

とかかな。これをうまく実装できるとかなりプログラムの柔軟性が上がりそうな予感がする。一方で、他のパターンに比べてなんとなく毛色が違うように感じなくもない。

実装。四則演算電卓。
http://dl.dropbox.com/u/7810000/code/design_pattern/interpreter.zip
なお、実装のBNF記法は下記サイトを参考にしてます。
http://d.hatena.ne.jp/yo-kun/20080522/p1
今回の実装のBNFは以下のようなつもり。

::= { + | - }*
::= { * | / }*
::= | ( )

実行。トークン分解はStringTokenizerに丸投げなため、式の各要素の間に必ずスペースを入れないとうまく動作しません。

$ ./run.sh
*** welcome to Calculator ***
plese type "q" if you want to exit calculator
calc> 2 * ( 4 + 3 / ( 3 - 1 ) )
INFO: parsed exp = "2.0*(4.0+3.0/(3.0-1.0))"
reuslt = 11.0
calc> q

ポイントはNodeインターフェースかな?四則演算で出てくる式、項、因数、数値はすべてこのNodeインターフェースの実装として書かれています。

public interface Node {
    public abstract void parse(Context context) throws ParseException;
    public abstract double getResult() throws CalcException;
}

実際に計算するcalculatorクラスでは、トップのノードからNodeインターフェースの関数を再帰的に呼ぶことで解析・計算をしている。

public class Calculator {
    private static Calculator instance = null;
    private Calculator() {}

    public static Calculator getInstance() {
        if (instance == null) instance = new Calculator();
        return instance;
    }

    public double calc(String exp) {
        Context context = new Context(exp);
        // ルートノード
        Node rootNode = new ExpNode();

        try {
            // 構文解析メソッドを再帰的に実行
            rootNode.parse(context);
        } catch (ParseException e) {
            System.out.println("ERROR: parseException: " + e.getMessage());
            return 0.0;
        }

        // ここのtoStringも再帰的に実行
        System.out.println("INFO: parsed exp = \"" + rootNode.toString() + "\"");

        double result = 0.0;
        try {
            // 計算メソッドも再帰的に実行
            result = rootNode.getResult();
        } catch (CalcException e) {
            System.out.println("ERROR: calcException: " + e.getMessage());
            return 0.0;
        }

        return result;
    }
}


仕事が忙しくなって休みの日にぐだぐだしてたせいでInterpreterパターンの実装が非常に遅くなったけど、なんとかこれでGoFの23パターンの実装が一段落したわけだ。よかったよかった。次はマルチスレッドのパターン・・・といくのが自然な流れな気もするけど、デザインパターン以外もやりたい今日この頃。