15.1 なぜ RWCollectable クラスを設計するのか?
RWCollectable クラスの設計方法の詳細に入る前に、あえて RWCollectable クラスを設計する理由を具体例で説明します。
例えば、バス会社を経営していると仮定しましょう。乗客記録システムの一部を自動化するために、バス、バスの利用者、実際の乗客を表わすような各クラスを作成したいとします。乗客になるためには、その人はバスの利用者でなければなりません。そのため、利用者群は、乗客群のスーパーセットになります。また、乗客は同時にバスに一度しか乗車することができないので、どの時点でも利用者リストに同じ人を 2 度以上載せることはできません。このシステムの開発者として、どちらのリストにも重複が許されないことを確実にしなければなりません。
これらの重複は問題を引き起こす可能性があります。このプログラムはバスとその利用者についての情報を保存し、それを復元できなければなりません。バスを多形的に保存するとき、プログラムが各人を保存しながら、単純に利用者群を一巡し、それから乗客群を一巡すると、利用者であると同時に乗客である者は 2 回保存されてしまいます。プログラムが多形的にバスを復元するとき、乗客のリストは利用者のリストにすでに載っている人を参照しなくなります。その代わり、各乗客は両方のリストに別々のインスタンスを持つことになります。
ある人がすでに多形的にストリームに保存されているとき、それを認識し、その人を再び保存するのではなく、単に前のインスタンスへの参照を保存するような方法が必要になります。
これがクラス RWCollectable の役目です。RWCollectable を継承するオブジェクトは、オブジェクトの内容だけでなく、RWCollectable を継承する他のオブジェクトとの関係も保存する能力を備えています。この機能を同形永続性と呼びます。RWCollectable クラスは同形永続性を持ちますが、それ以上の機能があります。つまり、実行時に保存されるあるいは復元されるオブジェクトの型を判断できます。RWCollectable によって提供されるこの種類の永続性を多形永続性と呼びます。
15.1.1 RWCollectable クラスの例
次のコードは、前の節で説明したクラスをどのように宣言するかを示しています。後でマクロ RWDECLARE_COLLECTABLE を使って、関数の選択について説明します。完全なコードは、この章の終わりに挙げられた例を見てください。また、toolexam ディレクトリにあるバスの例としても挙げられています。
class Bus : public RWCollectable {
RWDECLARE_COLLECTABLE(Bus)
public:
Bus();
Bus(int busno, const RWCString& driver);
~Bus();
// Inherited from class "RWCollectable":
Rwspace binaryStoreSize() const;
int compareTo(const RWCollectable*) const;
RWBoolean isEqual(const RWCollectable*) const;
unsigned hash() const;
void restoreGuts(RWFile&);
void restoreGuts(RWvistream&);
void saveGuts(RWFile&) const;
void saveGuts(RWvostream&) const;
void addPassenger(const char* name);
void addCustomer(const char* name);
size_t customers() const;
size_t passengers() const;
RWCString driver() const {return driver_;}
int number() const {return busNumber_;}
private:
RWSet customers_;
RWSet* passengers_;
int busNumber_;
RWCString driver_;
};
class Client : public RWCollectable {
RWDECLARE_COLLECTABLE(Client)
Client(const char* name) : name_(name) {}
private:
RWCString name_;
//ignore other client information for this example
};
両方のクラスが RWCollectable から派生していることに注意してください。重複項目を許可しないクラス RWSet を用いて、利用者群を実装することにしました。これにより、同じ人が利用者リストに 2 度以上入力されないことが保証されます。同じ理由で、RWSet クラスを用いて、乗客群を実装することにしました。ただし、こちらの乗客群はヒープ領域に設定することにしました。これについては、後の節で説明します。