PR
図1●数式を構文解析した結果の例
図1●数式を構文解析した結果の例
[画像のクリックで拡大表示]

 先日、某企業で、オブジェクト指向に関するセミナーの講師を務めてまいりました。とても基本的な概念から説明したので、ある程度の経験がある受講者は飽きてしまったようです。私の話を聞かずに、参考までに配布したGoFデザインパターンの資料を黙々と読んでいました。休み時間になると、その受講者が目を輝かせながら「デザインパターンって面白いですね!」と話しかけてきました。GoFデザインパターンを知ったことで、OOPの楽しさに目覚めたのでしょう。私は、とても嬉しい気持ちになりました。読者の皆様にも、もっともっとOOPを楽しんでいただけるように、今回も(いよいよ最終回です)がんばって2つのパターンを紹介します。

【お役立ち度】★★★☆☆
●ツリー構造で構文解析結果を表すInterpreterパターン

 「Interpreter(通訳)」パターンという名前を聞くと、「おおっ、オリジナルのプログラミング言語を作る方法だな!」と期待されるかもしれませんが、それほど大げさなものではありません。インタプリタには、構文解析と実行が必要になります。Interpreterパターンは、とりあえず構文解析は終わっているとして、その結果を実行する際の工夫です。滅多に必要とされないパターンだと思いますので、評価は星3つとさせていただきました。

 構文解析の結果は、ツリー構造になるものです。数式を解析する場合を例にしてみましょう。1+(2×3)という数式は、図1[拡大表示]のようなツリー構造で表せます。円で囲まれた節が処理で、そこから伸びた四角い枝がデータです。図を下から上に向かってたどってください。2と3が掛け算され、その結果と1が足し算されます。

 このツリー構造をプログラムで表すのに、Interpreterパターンでは、インターフェイスを使います。たとえば、実行を意味するexecuteメソッドを持つMyInterpreterインターフェイスを定義し、加算を表すAdditionクラス、乗算を表すMultiplierクラス、およびデータを表すDataクラスで、そのインターフェイスを実装します。Dataクラスのexecuteメソッドは、自分の値を返すだけです。AdditionクラスとMultiplierクラスでは、フィールドとしてデータと下位の節の参照を保持し、executeメソッドでは、下位の節のexecuteメソッドが返す値と自身のデータを演算します。

 具体的なサンプルコードは示しませんが、「あれ、このパターンどこかで見たことがあるぞ」と思われたでしょう。なかなか鋭いですね。Interpreterパターンは、第9回で紹介したCompositeパターンとよく似ています。違いは、パターンを使う目的にあります。Compositeパターンの目的は、たとえばファイルとディレクトリを同一視するようなことです。それに対して、Interpreterパターンの目的は、言語構文の追加に容易に対応することです。新たな演算子が定義された場合は、既存の演算子と同様にMyInterpreterインターフェイスを実装し、フィールドとして下位の節の参照を持てばよいのです。

【お役立ち度】★★★★
●形見を残してアンドゥ機能を実現するMementoパターン

 連載の最後を飾るのにふさわしいパターンを紹介しましょう。その名も「Memento(形見)」パターンです。このパターンは、オブジェクトの現在の状態を保存し、後で元の状態に復元するための工夫です。たとえば、ワープロやペイントソフトなどには、操作をキャンセルして元の状態に戻す「アンドゥ」機能がありますね。それと似たようなことを実現するのです。先ほどのInterpreterパターンよりは、使い道が多いと思いますので、評価は星4つとしました。

 ところで、オブジェクトの状態って何だかわかりますか? 簡単に言えば、フィールドの値のことです。メモリ上にロードされたオブジェクトは、いくつかのフィールドを持っています。プログラムの動作が進むと、それに応じてフィールドの値が変化して行きます。この様子をオブジェクトの状態が変化すると考えるわけです。

 それでは、どうすればオブジェクトの状態を保存できるでしょう。この連載をお読みの読者ならおわかりですね。オブジェクトのクローンを作って、コレクション(オブジェクトの配列)に格納しておけばよいのです。コレクションを実現する機能は、JavaならVectorクラス、.NETならArrayListクラスが提供してくれます。自らコーディングすることなど、ほとんどありません。簡単なものです。

 しかし、単にオブジェクトのクローンを作り、それをコレクションに保存したり、コレクションから復元するというだけでは、少し問題があります。それは、フィールドの一部だけを保存したい場合に対応できないことです。100個のフィールドの中で、保存したいのは10個のフィールドだけということもあるはずです。やっぱり、自らコーディングして工夫を加えないとダメみたいですね。

 Mementoパターンのサンプルコードをお見せしましょう。リスト1[拡大表示]は、fieldA、fieldB、およびfieldCという3つのフィールドを持つMyClassクラスです。他にもメンバがあるはずですが、ここでは「・・・」で省略しています。これら3つのフィールドの中で、オブジェクトの状態としてfieldAとfieldBだけを保存したいとします。そこで、fieldAとfieldBをメンバに持つMyMementoクラスを別に作ります。MyMementoクラスのオブジェクトが、MyClassクラスのオブジェクトの状態を保存する「ミメントオブジェクト」となるのです。ミメントオブジェクトを操作するメソッドを記述する場所には、3つの候補があります。MyClassクラス、MyMementoクラス、もしくは、それらのクラスを使う側のクラスです。どのクラスにすべきだと思いますか?

リスト1●保存したいフィールドだけを持つクラスを別に作る

public class MyClass {
    private int fieldA; // 保存する
    private int fieldB; // 保存する
    private int fieldC; // 保存しない
    ・・・
    }
}

public class MyMemento {
    public int fieldA;
    public int fieldB;

    public MyMemento (int a, int b) {
        this.fieldA = a;
        this.fieldB = b;
    }
}

 答えは、MyClassクラスです。なぜなら、MyClassクラスのフィールドは、privateでカプセル化されているからです。保存したいフィールドを自由に読み書きできるのは、MyClassクラスのメソッドだけということになります。先ほど、他にもメンバがありますと言って「・・・」で省略していた部分には、ミメントオブジェクト作るメソッドと、ミメントオブジェクトからフィールドの値を復元するメソッドを記述することになるのです。「うわぁ、面倒くさいなぁ~」と思いますよね。確かに、MyClassクラスやMyMementoクラスを作る人は面倒です。でも、それらのクラスを使う人は楽ができるのですから、いいじゃないですか。オブジェクト指向で大事なことは、何たって「クラスを使う人への思いやり」です。

●おわりに

 これで、23種類のGoFデザインパターンの紹介が、すべて終わりました。いろいろなパターンを知って、どう感じましたか? 継承、カプセル化、多態性といったOOPのテクニックの便利さを再認識し、自分のプログラムの中でデザインパターンを活用したくなったでしょう。そう思ったら、すぐに実践してください。有名なアルゴリズムと同様に、有名なデザインパターンは再利用できることに価値があるのです。そう言えば、GoF本のタイトル(Design Patterns : Elements of Reusable Object Oriented Software)にも「再利用可能(Reusable)」という言葉がバッチリ入ってますね!

 末筆になりましたが、素晴らしいデザインパターンのアイディアを示してくれたthe Gang of Fourの皆様、様々な場面でお世話になった編集部の皆様、そして何より連載をお読みいただいた読者の皆々様に、この場をお借りして厚く御礼申し上げます。

矢沢久雄

グレープシティ株式会社(http://www.grapecity.com)アドバイザリースタッフ

表紙ページへ