15.3 委任ベースのインタフェース実装

委任ベースのインタフェース実装は、CORBAオブジェクトを実装する際の継承の使用に代わる代替手法です。この手法は、継承のオーバーヘッドが大きすぎたり、使用できなかったりする場合に使用します。たとえば、一部のグローバル・クラスに継承が必要な場合に既存のレガシー・コードを使用すると、継承の侵襲的な性質が原因で、オブジェクトを実装できないことがあります。デレゲーションは、この種の問題を解決するために使用できます。デレゲーションを使用すると、Process-Entityデザイン・パターンにより、オブジェクトをより自然に実装できます。このパターンでは、Processオブジェクトが1つまたは複数のエンティティ・オブジェクトに操作を委任します。

委任ベースの手法では、実装はスケルトン・クラスから継承しません。かわりに、実装はアプリケーションの必要に応じてコーディングでき、ラッパー・オブジェクトがその実装に呼出しを委任します。この「ラッパー・オブジェクト」は「tie」と呼ばれ、継承手法で使用される同じスケルトン・クラスと共にIDLコンパイラによって生成されます。生成されたtieクラスは、スケルトンと同様に、関連付けられたインタフェースの各OMG IDL操作に対応するメソッドを備えていますが、プログラマにとっては非透過的な部分もあります。生成されたtieクラスの名前は、生成されたスケルトン・クラスと同様に、クラス名の末尾に文字列_tieが付け加えられます。

tieクラスのインスタンスはサーバントで、tieオブジェクトによって委任されるC++オブジェクトではありません。このサーバントは、Servant引数を必要とする操作に引数として渡されます。また、関連付けられたオブジェクトは、_this( )操作にアクセスしたり、直接データ・メンバーにアクセスしたりすることはできません。

型保障tieクラスは、C++テンプレートを使用して実装されます。次のコード・スニペットでは、前のOMG IDL例の導出インタフェースから生成されたtieクラスについて説明します。

// C++
template <class T>
class POA_A_tie : public POA_A {
public:
   POA_A_tie(T& t)
       : _ptr(&t), _poa(PortableServer::POA::_nil()), _rel(0) {}
   POA_A_tie(T& t, PortableServer::POA_ptr poa)
       : _ptr(&t), _poa(PortableServer::POA::_duplicate(poa)), _rel(0) {}
   POA_A_tie(T* tp, CORBA::Boolean release = 1)
       : _ptr(tp), _poa(PortableServer::POA::_nil()), _rel(release) {}
   POA_A_tie(T* tp, PortableServer::POA_ptr poa, CORBA::Boolean release = 1)
       : _ptr(tp), _poa(PortableServer::POA::_duplicate(poa)), _rel(release) {}
   ~POA_A_tie()
   { CORBA::release(_poa);
   if (_rel) delete _ptr;
   }
   // tie-specific functions
   T* _tied_object () {return _ptr;}
   void _tied_object(T& obj)
{ if (_rel) delete _ptr;
_ptr = &obj;
_rel = 0;
}
void _tied_object(T* obj, CORBA::Boolean release = 1)
{ if (_rel) delete _ptr;
_ptr = obj;
_rel = release;
}
CORBA::Boolean _is_owner() { return _rel; }
void _is_owner (CORBA::Boolean b) { _rel = b; }
// IDL operations*************************************
CORBA::Short op1 ()
{
   return _ptr->op1 ();
}
void op2 (CORBA::Long val)
{
   _ptr->op2 (val);
}
// ***************************************************
// override ServantBase operations
PortableServer::POA_ptr _default_POA()
{
     if (!CORBA::is_nil(_poa))
     {
         return _poa;
     }
     else {
#ifdef WIN32
           return ServantBase::_default_POA();
#else
           return PortableServer::ServantBase::_default_POA();
#endif
         }
   }
private:
    T* _ptr;
    PortableServer::POA_ptr _poa;
    CORBA::Boolean _rel;
   // copy and assignment not allowed
   POA_A_tie (const POA_A_tie<T> &);
   void operator=(const POA_A_tie<T> &);
};

このクラス定義は、IDLコンパイラによって生成されたテンプレートです。通常、このテンプレートを使用するには、レガシー・クラスに対するポインタを取得してから、tieクラスをそのポインタでインスタンス化します。例:

Old::Legacy * legacy = new Old::Legacy( oid);
POA_A_tie<Old::Legacy> * A_servant_ptr =
             new POA_A_tie<Old::Legacy>( legacy );

ご覧のとおり、tieクラスにはインタフェースのop1操作とop2操作の定義があります。これらの操作は、レガシー・クラスにはIDLと同じシグネチャの操作があることを想定しています。この想定どおりの場合、tieクラス・ファイルをそのまま使用でき、正確に委任できます。ただし、レガシー・クラスのシグネチャが異なっていたり、単一の関数呼出しを複数回にわたって行わなければならない場合も多くあります。このような場合は、生成されたコードのop1とop2のコードを置き換えてください。通常、各操作のコードでは、tieクラス変数_ptrを使用してレガシー・クラスを呼び出します。このクラス変数には、レガシー・クラスに対するポインタが格納されます。たとえば、次のように行を変更するとします。

CORBA::Short op1 () {return _ptr->op1 (); }
void op2 (CORBA::Long val) {_ptr->op2 (val); }

変更後:

CORBA::Short op1 ()
{
    return _ptr->op37 ();
}
void op2 (CORBA::Long val)
{
   CORBA::Long temp;
   temp = val + 15;
   _ptr->lookup(val, temp, 43);
}

このテンプレート・クラスのインスタンスは、委任のタスクを実行します。Derivedインタフェースの操作を提供するクラス型でテンプレートがインスタンス化された場合、POA_Derived_tieクラスは、その実装クラスのインスタンスに操作をすべて委任します。実際の実装オブジェクトに対するリファレンスまたはポインタは、POA_Derived_tieクラスが作成されるときに適切なtieコンストラクタに渡されます。そこでリクエストが呼び出されると、tieサーバントは実装クラスの対応するメソッドを呼び出してリクエストを委任します。

tieクラスのテンプレートを使用すると、アプリケーション開発者は、テンプレートの指定のインスタンス化用にテンプレートの操作の一部またはすべてを特化することができます。これにより、アプリケーションでは関連付けられたオブジェクト型のレガシー・クラスを使用できるようになります。この場合、関連付けられたオブジェクトのシグネチャは、tieクラスのシグネチャとは異なります。