このアーカイブは同期化されません。 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 との対照性を考えるとあるほうが良い。