2006 年 12 月 16 日 23 時 25 分

複雑なストリームの問題


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


しばらくは基礎の実装が続くので、
プロトコルの解説は少しおいといてほしい。

RFB 通信経路から基本型を読み出すために、
RFBInputStream というクラスを作ることにしよう。

基本型がビッグエンディアンの整数型なので、
DataInputStream を継承するか委任すれば速そうだ。

また、RFB のメッセージは可変長であり、
受信する順序に関する制約はあまりない。
そのため、ストリームから読み出したメッセージが、
どのような種類のものかどうかを調べるためには、
ある程度先読みをしなければならないケースがある。

それを考えると、mark をサポートする
BufferedInputStream も利用したい。

Java で両クラスをうまく活用して使うためには、
継承だけでなく、委任の仕組みを併用する必要がある。

委任(委譲・委託) とは、クラスのメソッドの実装を、
別クラスのインスタンスに代行させることである。
通常、委任される側のインスタンスは、
委任する側がフィールドとして持つことが多い。

継承を使った場合、派生クラスは、
基底クラスの一種として扱われるので、
利用者に基底メソッドが全て公開されるのだが、
委任の場合、委任先クラスは一切見えない。
また継承と違い、選択的にメソッドを公開することができる。

では、これを元に RFBInputStream を考えてみよう。

DataInputStream も BufferedInputStream も、
FilterInputStream から派生したクラスだ。
FilterInputStream は元となる InputStream を受け取り、
それに何かしらの機能を追加するため、
元となる InputStream に直接アクセスしてはいけない。

なので、これらの恩恵を受けるためには、
BufferedInputStream に基準のストリームを渡し、
さらに DataInputStream に BufferedInputStream を渡す。
そして、DataInputStream を使って実装すればよいのだ。

となると、DataInputStream を継承すれば速いのだが、
DataInputStream をそのまま継承してしまうと、
関係ないメソッドまで全て公開されてしまうため良くない。
そのため、DataInputStream は委任で使うことになる。

外向けには RFBInputStream はストリームの一種であり、
基底となるストリームに機能を追加するタイプなので、
FilterInputStream であることを宣言するのが好ましい。
これは、FilterInputStream を継承する事で明示できる。

以上を踏まえると、以下のような枠組みとなる。

public class RFBInputStream extends FilterInputStream {

    public RFBInputStream(InputStream in) {
        super(new DataInputStream(new BufferedInputStream(in)));
    }

}

FilterInputStream は、in というフィールドを持っており、
コンストラクタで渡された引数を格納している。
また InputStream の実装を全て in に委任するため、
継承するだけで InputStream の実装も完了したことになる。

FilterInputStream は、一切のバッファ処理を行わないため、
FilterInputStream のフィールドである in に対して、
直接呼び出しを行っても問題はないことが保障される。

in の実体は DataInputStream であるため、
in を DataInputStream にキャストすることで、
DataInputStream のメソッドも利用できるというわけだ。



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