インターフェースクラスからのシリアライズ




ICounterで見たように、ICounterというインターフェースに対して、

枠-1
    CRootCounter
    CSaturationCounter
    CInteriorCounter

という複数の実装があります。ICounter自体は、IArchive派生クラスなので、シリアライズ能力自体はありますが、動的な型がわからないことには、その型を生成することが出来ません。

そこで、RTTI的な機能によって、型を取得して、シリアライズするときに、その型も保存しておき、デシリアライズ(シリアライズしたデータを復元すること)のときには、その型から、factoryを用いて、その型を生成し、しかるのちにデシリアライズするという機構があれば便利です。

これは、CProxyCounterというクラスが担います。yaneSDK3rdで、あるインターフェースクラスに対して、「CProxyなんとか」というクラスが存在したなら、これは、シリアライズ機構を提供するための入れ物、だと考えると良いです。

CProxyCounterの実装を見ていきましょう。まず、CProxyCounterはICounter派生クラスです。ICounterはIArchive派生クラスですから、CProxyCounterは、シリアライズ機構を有します。

CProxyCounterは、そのコンストラクタで、

枠-2
    CProxyCounter() : m_vCounter(new CNullCounter) {}
    CProxyCounter(const smart_ptr<ICounter>& p) : m_vCounter(p){}


というように、カウンタを取得します。引数無しコンストラクタのほうでnewしているのは、俗にNullオブジェクトと呼ばれるもので、ICounterの、何もしない実装です。インターフェースクラスに対して、「CNullなんとか」というクラスが定義されている場合、それは、Nullオブジェクトです。何のためにこんなクラスが必要かというと、普通proxyパターンでポインタを通じてメソッドを呼び出すときには、Nullチェックが必要なのですが、そのNullチェックを行なうのが嫌だからディフォルトでNullオブジェクトを突っ込んであるのです。そうすれば、Nullチェックを回避できます。(⇒詳しくは、「やね本1」参照のこと)

CProxyCounterのシリアライズ機構は、以下のように
1.シリアライズ時には、ICounterの動的な型を取得して、それをデータとして埋め込む
2.デシリアライズ時には、動的な型から、factoryによって、それを生成する

というものです。

list-1
void CProxyCounter::Serialize(ISerialize&s){
    if (s.IsStoring()){
        int nType = GetCounter()->GetType();
        s << nType; //  RTTI
        s << *GetCounter();
    } else {
        int nType;
        s << nType;         //  型からfactoryによって復元
        m_vCounter.Add(ICounter::CreateInstance(nType));
        s << *GetCounter();
    }
}


見てのとおり、factory自体は、ICounter::CreateInstanceです。このように、インターフェースクラス自体が、factoryを持っています。また、このfactoryは、あとからユーザーが追加したICounterの具象クラス(派生クラス)を生成するためのfactoryを事後登録するために、次のような仕組みを持っています。

枠-3 ICounterのメソッド
    static  void    SetFactory(const smart_ptr<ICounterFactory>& v)
        {   m_vFactory = v; }
    static  smart_ptr<ICounterFactory>  GetFactory()
        { return m_vFactory; }


このICounterFactoryというのは、次のようなインターフェースクラスです。

枠-4
class ICounterFactory {
/**
    ユーザーで独自にカウンタ(ICounter派生クラス)を用意する場合、
    GetTypeで、1000以上のuniqueな数字を返すようにして、
    このFactory派生クラスで、CreateInstanceをオーバーライドして、
    その数字を受け取ったときに、そのクラスをnewして返すようにして、
    そのFactoryを、ICounter::SetFactoryすれば良い。

    そうすれば、CProxyCounterによる、その新しく作ったカウンタの
    シリアライズが可能となる。
*/
public:
    virtual ICounter* CreateInstance(int n)=0;

    virtual ~ICounterFactory(){}
};


このように、ICounterというインターフェースクラスに対して、

枠-5
1.IArchive派生クラスにする
2.GetTypeという、動的な型を取得するメソッドを用意する
3.CNullCounterというNullオブジェクトを用意する
4.CProxyCounterという、シリアライズ能力を有する代理オブジェクトを用意する
5.ICounterにICounter具象クラスのfactoryを用意する
6.ICounterにfactoryを追加登録する機能を持たせる


を行なうことによって、はじめて、完全なシリアライズ機能を持たせることが出来るわけです。(ちょっと面倒ですが、まあ仕方ないです)

yaneSDK3rdでは、インターフェースクラスは、可能な限り上記の流儀に従っています。