図1●このアイディアで、基本的にOKだが...
図1●このアイディアで、基本的にOKだが...
[画像のクリックで拡大表示]

 プログラムの動作を分析する際には、大きく分けて2つの視点があると思います。1つは「処理が流れて行く」という見方です。これを図に表すと、フローチャートになります。もう1つは「状態が変化して行く」という見方です。これを図に表すと、状態遷移図になります。これら2つの視点は、プログラムに置き換える対象に応じて自然と使い分けられるものです。

 プログラムの状態と聞くと、ピンと来ないかもしれませんね。早い話が、データの値が様々に変化することです。オブジェクト指向プログラミングでは、フィールドの値がオブジェクトの状態です。オブジェクトは、現在の自分の状態に応じた振る舞いをします。場合によっては、状態が変化したことを、他のオブジェクトに通知することもあります。その際に、何か工夫をしたくてウズウズしてきませんか。

【お役立ち度】★★★★
●if文を使わずに、状態に応じた処理を行えるStateパターン

 3つの状態があって、それぞれの状態に応じて異なる処理をするプログラムを作るとしたらどうしますか? たとえば、朝、昼、夜という状態があって、それぞれ「おはよう!」「こんにちは!」「こんばんは!」というメッセージを表示するとしたら。ちょっと考えてみてください。

 もしも、筆者が、初心者向けのプログラミング講座で見本を示すとしたら、朝、昼、夜という状態を何らかの数値で表し、状態を引数で受け取るメソッドを作り、その中でif文を使って処理を分けるでしょう。このやり方は、間違っていません。でも、もっと状態の種類が多かったり、それぞれの状態における処理がもっと複雑だったら、メソッドの内容が長たらくグチャグチャになってしまいますね。

 そこで、一工夫です。状態を表す複数のクラスを作ってみましょう。この工夫は「State (状態)パターン」と呼ばれます。たとえば、メッセージを表示するshowMessageメソッドを持つMyStateクラスを作ります。このメソッドは、処理内容のない抽象メソッドとします。MyStateクラスを継承して3つの状態を表すMornigStateクラス、DayStateクラス、NightStateクラスを作り、showMessageメソッドの処理内容をクラスごとに実装(処理内を記述)します。showMessageメソッドには、引数が不要です。なぜだかわかりますか。クラスの種類そのものが、状態を表しているからです(リスト1[拡大表示])。

リスト1●3つの状態を表すクラスを作りメソッドを実装する

// 抽象クラス
public abstract class MyState {
    ・・・
    public abstract void showMessage();
}

// 「朝」を表すクラス
public class MornigState extends MyState {
    ・・・
    public void showMessage() {
        System.out.println("おはよう!");
    }
}

// 「昼」を表すクラス
public class DayState extends MyState {
    ・・・
    public void showMessage() {
        System.out.println("こんにちは!");
    }
}

// 「夜」を表すクラス
public class NightState extends MyState {
    ・・・
    public void showMessage() {
        System.out.println("こんばんは!");
    }
}

 クラスを使う人は、あらかじめ3つの状態を表すクラスのオブジェクトを作り、変化する状態を保持する変数currentStateを用意しておきます。currentState.showMessage(); を実行すれば、現在の状態に応じたメッセージが表示されます。if文なんか使いません(リスト2[拡大表示])。

リスト2●現在の状態に応じたメッセージが表示される

MyState mornig = new MornigState();
MyState day = new DayState();
MyState night = new NightState();
・・・
MyState currentState;
・・・
currentState = mornig;
currentState.showMessage();    // おはよう!
・・・
currentState = day;
currentState.showMessage();    // こんにちは!
・・・
currentState = night;
currentState.showMessage();    // こんばんは!

 このように、見た目には同じcurrentState.showMessage(); で様々なメソッドが呼び出されるテクニックを「多態性」と呼びます。ちょっと難しいことなので、次に紹介するパターンともども、評価は星4つとしておきましょう。

【お役立ち度】★★★★
●複数のオブジェクトに状態の変化を通知するObserverパターン

 「Observer(観察者)パターン」は、状態が変化したことを他のオブジェクトに通知するための工夫です。簡単に言えば、オブジェクトが何らかの振る舞い(メソッドの実行)をして、それによって状態が変わった(フィールドの値が変わった)ことを、別のオブジェクトに知らせるには、どうしたらよいかということです。これも、ちょっと考えてみてください。

 すぐに思いつくアイディアは、通知先のオブジェクトに通知専用メソッドを定義しておき、それを通知元のオブジェクトが呼び出すことです。変化した状態の値は、メソッドの引数で渡せばいいでしょう。図1[拡大表示]では、currentStateが通知したい状態で、noticeが通知専用メソッドになっています。

 このアイディアのままでも、基本的にOKです。でも、もう少しだけ工夫をしてみましょう。状態が変化したことを、複数のオブジェクトに通知したいこともあるはずです。そのとき、通知専用メソッドの名前がオブジェクトごとに異なっていたら面倒ですね。それなら、通知元のオブジェクトが「私からの通知を受け取りたいなら、void notice(int s); というメソッドを必ず実装しなさい!」と強制すればいいいのです。

 メソッドの強制実装のために使われるプログラミングテクニックが「インターフェイス」です。インターフェイスは、抽象メソッドだけを定義したクラスのようなものですが、クラスとは別の構文があります。リスト3[拡大表示]は、抽象メソッドvoid notice(int s); を定義したインターフェイスです。このインターフェイスを通知先のすべてのオブジェクトでインプリメントすれば、それぞれvoid notice(int s); の実装が必須となります。つまり、通知先のすべてのオブジェクトが、必ずvoid notice(int s); というメソッドを持つことになります。

リスト3●通知専用メソッドを持つインターフェイスの定義

interface MyObserver {
    void notice(int s);
}

 通知元のオブジェクトでは、MyObserverインターフェイスの配列(MyObserver[] obj;)として、通知先の複数のオブジェクトの参照(ポインタ)を保持しておきます。配列のデータ型をMyObserverインターフェイスとすることがポイントです。「通知先のオブジェクトは、どんなクラスのオブジェクトでも構わないが、少なくともMyObserverインターフェイスをインプリメントしているので、void notice(int s); という通知専用メソッドを実装しているはずだ。したがって、通知元の私は、obj[i].notice(currentState); という構文で状態の変化を通知できる」ということになります。めでたし、めでたしです。

 多態性やインターフェイスは、オブジェクト指向プログラミングの学習ゴールと呼べる高度なテクニックです。それらを習った時点では、何が便利なのかわからなかったかもしれませんが、GoFのパターンの中でバンバン応用されていることを知ると「なるほど、多態性やインターフェイスとは、実に便利なものだ!」と感動していただけるはずです。

 次回は、SingletonパターンとFlyweightパターンを紹介します。

矢沢久雄

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

表紙ページへ