2012-02-10 36 views
7

明示的なインタフェース(ケース1;抽象クラスへのポインタ)を使用した暗黙のインタフェース(ケース2と3;テンプレート)の使用の利点/欠点は次のとおりですか?暗黙的対明示的なインタフェース

変化しないコード:

class CoolClass 
{ 
public: 
    virtual void doSomethingCool() = 0; 
    virtual void worthless() = 0; 
}; 

class CoolA : public CoolClass 
{ 
public: 
    virtual void doSomethingCool() 
    { /* Do cool stuff that an A would do */ } 

    virtual void worthless() 
    { /* Worthless, but must be implemented */ } 
}; 

class CoolB : public CoolClass 
{ 
public: 
    virtual void doSomethingCool() 
    { /* Do cool stuff that a B would do */ } 

    virtual void worthless() 
    { /* Worthless, but must be implemented */ } 
}; 

ケース1:明示的なインタフェースを提供する基底クラスのポインタをとる非テンプレートクラス:

class CoolClassUser 
{ 
public: 
    void useCoolClass(CoolClass * coolClass) 
    { coolClass.doSomethingCool(); } 
}; 

int main() 
{ 
    CoolClass * c1 = new CoolA; 
    CoolClass * c2 = new CoolB; 

    CoolClassUser user; 
    user.useCoolClass(c1); 
    user.useCoolClass(c2); 

    return 0; 
} 

ケース2:

template <typename T> 
class CoolClassUser 
{ 
public: 
    void useCoolClass(T * coolClass) 
    { coolClass->doSomethingCool(); } 
}; 

int main() 
{ 
    CoolClass * c1 = new CoolA; 
    CoolClass * c2 = new CoolB; 

    CoolClassUser<CoolClass> user; 
    user.useCoolClass(c1); 
    user.useCoolClass(c2); 

    return 0; 
} 

ケース3:テンプレートを持つクラステンプレートのクラス

class RandomClass 
{ 
public: 
    void doSomethingCool() 
    { /* Do cool stuff that a RandomClass would do */ } 

    // I don't have to implement worthless()! Na na na na na! 
}; 

template <typename T> 
class CoolClassUser 
{ 
public: 
    void useCoolClass(T * coolClass) 
    { coolClass->doSomethingCool(); } 
}; 

int main() 
{ 
    RandomClass * c1 = new RandomClass; 
    RandomClass * c2 = new RandomClass; 

    CoolClassUser<RandomClass> user; 
    user.useCoolClass(c1); 
    user.useCoolClass(c2); 

    return 0; 
} 

ケース1は、オブジェクトが useCoolClass(に渡されることを必要とする)はCoolClassの子である(および価値を実装(:食べたタイプではないCoolClass由来する暗黙のインターフェース(この時間を、提供します))。一方、ケース2および3は、のいずれかのクラスを持ち、 doSomethingCool()関数を持ちます。

コードのユーザーは常にCoolClass細かいサブクラス化た場合 CoolClassUserは常に CoolClassの実装を期待されるので、その後、ケース1は、直感的に理にかなっています。しかし、このコードがAPIフレームワークの一部であると仮定して、ユーザーが CoolClassのサブクラス化を行うか、または doSomethingCool()関数を持つ独自のクラスをロールするかどうかは予測できません。

いくつかの関連記事:

https://stackoverflow.com/a/7264550/635125

https://stackoverflow.com/a/7264689/635125

https://stackoverflow.com/a/8009872/635125

+0

ケース1とケース2はコンパイルされません。ポインタの初期化は間違った方法で行われます。 – Novelocrat

+0

@Novelocratが修正されました。 –

答えて

3

あなたはケース1を好むなかった理由のために私の心に来たいくつかの注意事項:

  • の場合CoolClassは純粋なインターフェースではなく、実装の一部も継承されています(ただし、ケース2/3でも提供する可能性があります。ベースクラスの形式で)。
  • CoolClassUserをヘッダーではなくバイナリーで実装する理由がある場合(保護だけでなく、コードサイズ、リソースの制御、集中エラー処理なども可能です)。
  • ポインタを保存して後で使用したい場合は、ケース1も同じです:(a)すべてを同じコンテナに保管する方が簡単です。(b)実際のデータ型をケース2/3では、テンプレートラッパーの助けを借りて、それを「明示的」インターフェース(ケース1)に変換することが考えられます。ケース2/3 preferrableかもしれない理由

理由:

  • 後でworthless()は今価値のあるものであることを決定し、それを使用して起動した場合、ケース2に、あなたはコンパイル時クラスのエラーが発生しますどこに実装されていません。ケース1では、あなたが運が良ければランタイムエラーを除いて、これらの関数を実際に実装するように思い出させるものはありません。
  • より大きいコードサイズを犠牲にしても、Case2/3のパフォーマンスはわずかに向上する可能性があります。

場合によっては、個人または個人の好みの問題である場合があります。

1

#2と#3のケースでは、テンプレートパラメータに依存していることに注意してください。つまり、コール時のコーダはテンプレート引数を正しい型で正しくインスタンス化する必要があります。関数がどのように使用されるかに応じて、渡されるオブジェクトの型を気にすることなく、ユーザーのための抽象的なインタフェースを作成する必要があるいくつかの問題が発生する可能性があります。つまり、 "ハンドル" 1つのAPI関数から別のAPI関数にオブジェクトを渡すために多態性を使用している派生オブジェクトへの他のポインタ。例えば:

class abstract_base_class; 

abtract_base_class* get_handle(); 
void do_something_with_handle(abstract_base_class* handle); 
void do_something_else_with_handle(abstract_base_class* handle); 
//... more API functions 

、あなたのAPIフレームワークが戻ってあなたのコードのユーザーにオブジェクトを渡すことができ、そして、彼らはそのオブジェクトが何であるかを知っている必要はありません...彼らは、それが記述することを知っておく必要がありいくつかのタイプのインターフェースです。ヘッダーのどこかに公開することができます。しかし、彼らはあなたがそれらに戻ってきたオブジェクトの "勇気"について何も知る必要はありません。実装を制御するいくつかの派生型へのポインタをそれらに与えることができます。 APIの中で最も一般的なタイプの関数用のテンプレートを用意するだけで済みます。さもなければ、abstract_base_class*をとるためだけに設計された関数のテンプレートをインスタンス化しなければならないので、ユーザーが入力するためのより定型的なコードを作るだけです。

関連する問題