2006 年 12 月 21 日 23 時 57 分

メッセージクラスの設計


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


今後、メッセージが数多く出てくるので、
今のうちにメッセージ周りの設計をしよう。

以前述べたように、メッセージには色々な種類があるため、
基本型のように RFBInputStream と RFBOutputStream に
個別のメソッドを設けるのは現実的ではない。

そこで、メッセージには、専用のインタフェースを設け、
メッセージ自身によってデータの読み書きを行わせ、
統一した方法で取り扱えるようにするのが良い。

ストリームからメッセージを読み出すと言うことは、
Java における Serializable や Externalizable などの、
永続化手法が使えそうにも思えるが、
これらはクラスの識別子を追加してしまうのでだめだ。

また、メッセージは RFB のバージョンや通信状態によって、
構成要素の定義が異なっている場合がある。
メッセージ自体の役割は変わらないので、
これらの違いをメッセージ自身に吸収させれば楽だ。

これらの考察を元に、インタフェースを設計してみよう。

========== Message.java ==========

package jp.loafer.rfb.message;
package jp.loafer.rfb.message;

import java.io.IOException;

import jp.loafer.rfb.RFBContext;
import jp.loafer.rfb.io.RFBInputStream;
import jp.loafer.rfb.io.RFBOutputStream;

/**
* RFB メッセージの基底インタフェース。
* このインタフェースを実装するクラスは、
* 引数なしのコンストラクタを用意する必要がある。
* @author kes
*/
public interface Message {
   
    /**
     * メッセージをストリームから読み込む。
     * @param context 通信コンテキスト。
     * @param in 出力ストリーム。
     * @throws IOException 入力エラー。
     */
    void read(RFBContext context, RFBInputStream in) throws IOException;

    /**
     * メッセージをストリームに書き出す。
     * @param context 通信コンテキスト。
     * @param out 出力ストリーム。
     * @throws IOException 出力エラー。
     */
    void write(RFBContext context, RFBOutputStream out) throws IOException;

}

========== end of Message.java ==========

永続化されるインスタンスの状態は、
外部から context 引数として与えられるようにしておいた。
これにより、ストリームには余計なデータが流れず、
完全にクラス自身によってデータの管理が可能となる。

また、インタフェースでは実装の強制はできないが、
Message インタフェースを実装するクラスは、
引数なしのコンストラクタを持つ決まりを作っておこう。
これを定めておけば、ストリーム側にメッセージ入出力用の
補助メソッドを持たせることができるのだ。

========== RFBInputStream#readMessage ==========

    /**
     * メッセージを読み込む。
     * @param <T> メッセージの型。
     * @param context 通信コンテキスト。
     * @param messageClass メッセージを表すクラス。
     * @return messageClass のインスタンス。
     * @throws IOException
     */
    public <T extends Message> T readMessage(
            RFBContext context, Class<T> messageClass) throws IOException {
        try {
            T m = messageClass.newInstance();
            m.read(context, this);
            // System.out.println(m);
            return m;           
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new IOException("Unexpected error.", e);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new IOException("Unexpected error.", e);
        }
    }

========== end of RFBInputStream#readMessage ==========

========== RFBOutputStream#writeMessage ==========

    /**
     * メッセージを書き出す。
     * @param context 通信コンテキスト。
     * @param message メッセージ。
     * @throws IOException
     */
    public void writeMessage(RFBContext context,
            Message message) throws IOException {
        message.write(context, this);
        // System.out.println(message);
    }

========== end of RFBOutputStream#writeMessage ==========

このようにしておくと、少し使いやすくなる。
readMessage はクラス型を渡すだけで読み出せるようになる。
Generics のお陰で、呼び出し側でもキャスト不要だ。

writeMessage の方はあまり意味がないが、
readMessage との対照性を考えるとあるほうが良い。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.