株式会社エス・スリー・フォー

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 クラスを用いて、乗客群を実装することにしました。ただし、こちらの乗客群はヒープ領域に設定することにしました。これについては、後の節で説明します。