8

プログラムで特定の役割を果たすために特定のクラスがどのように動作するかを定義するインターフェイスを使用している状況がありますが、現時点では、その役割を満たすためにいくつのクラスを作成するかは 100% わかりません。 . ただし、同時に、特定の役割を満たすために使用するインターフェイスを実装する具体的なクラスを、GUI コンボ/リスト ボックスからユーザーが選択できるようにしたいこともわかっています。利用可能なすべてのクラスを GUI で列挙できるようにしたいのですが、その役割を担う新しいクラスを実装することを決定するたびに、古いコードに戻って変更する必要はありません (今から数か月後かもしれません)。

私が検討したいくつかのこと:

  1. 列挙の使用
    • 長所:
      1. 私はそれを行う方法を知っています
    • 短所
      1. 新しいクラスを追加するときに、列挙を更新する必要があります
      2. 繰り返すのが面倒
  2. インターフェイスである種のstaticリスト オブジェクトを使用し、実装クラスの定義ファイル内から新しい要素を追加する
    • 長所:
      1. 古いコードを変更する必要はありません
    • 短所:
      1. これが可能かどうかさえわからない
      2. ファクトリ メソッドが適切なコンストラクタを選択できるように、どのような種類の情報を保存すればよいかわからない (おそらく、文字列と、インターフェイスのオブジェクトへのポインタを返す関数ポインタとの間のマップ)

これは、経験豊富なプログラマーがおそらく以前に (そして頻繁に) 遭遇した問題 (または同様の問題) であり、この種の問題にはおそらく共通の解決策があり、それは私が考えた何よりも確実に優れていると思います。思いつくことができます。それで、どうすればいいですか?

(PS私が検索しましたが、私が見つけたのはこれだけで、同じではありません:ジェネリックインターフェイスを実装するすべてのアイテムを列挙するにはどうすればよいですか?。彼は私が理解しようとしている問題を解決する方法をすでに知っているようです。)

編集:タイトルの名前を「どうすれば...を列挙できますか...」ではなく「どうすれば追跡できますか...」に変更しました私が本当に興味を持っているのは、コンパイル時の簿記です。

4

6 に答える 6

8

クリエーター関数へのポインターを使用してクラスを登録できるシングルトンを作成します。具象クラスの cpp ファイルでは、各クラスを登録します。
このようなもの:

class Interface;
typedef boost::function<Interface* ()> Creator;

class InterfaceRegistration
{
    typedef map<string, Creator> CreatorMap;
public:
    InterfaceRegistration& instance() {  
        static InterfaceRegistration interfaceRegistration;
        return interfaceRegistration;
    }

    bool registerInterface( const string& name, Creator creator )
    {
        return (m_interfaces[name] = creator);
    }

    list<string> names() const
    {
        list<string> nameList;  
        transform(
            m_interfaces.begin(), m_interfaces.end(), 
            back_inserter(nameList) 
            select1st<CreatorMap>::value_type>() );
    }

    Interface* create(cosnt string& name ) const 
    { 
        const CreatorMap::const_iterator it 
            = m_interfaces.find(name);  
        if( it!=m_interfaces.end() && (*it) )
        {
            return (*it)();
        }
        // throw exception ...
        return 0;
    }

private:
    CreatorMap m_interfaces;
};


// in your concrete classes cpp files
namespace {
bool registerClassX = InterfaceRegistration::instance("ClassX", boost::lambda::new_ptr<ClassX>() );
}

ClassX::ClassX() : Interface()
{
    //....
}

// in your concrete class Y cpp files
namespace {
bool registerClassY = InterfaceRegistration::instance("ClassY", boost::lambda::new_ptr<ClassY>() );
}

ClassY::ClassY() : Interface()
{
    //....
}
于 2009-08-11T15:34:39.250 に答える
3

何年も前にこれに似たようなことをしたことを漠然と覚えています。あなたのオプション(2)は私がやったことです。その場合はstd::mapstd::stringtoでしたstd::typeinfo。それぞれの .cpp ファイルで、次のようにクラスを登録しました。

static dummy = registerClass (typeid (MyNewClass));

registerClasstype_infoオブジェクトを受け取り、単純に を返しますtrueregisterClass起動時に確実に呼び出されるように、変数を初期化する必要があります。registerClassグローバル名前空間で呼び出すだけではエラーになります。また、dummy静的にすることで、名前の衝突なしにコンパイル単位間で名前を再利用できます。

于 2009-08-11T15:22:58.510 に答える
2

この記事を参照して、TimW の回答で説明されているものと同様の自己登録クラス ファクトリを実装しましたが、テンプレート化されたファクトリ プロキシ クラスを使用してオブジェクトの登録を処理するという優れたトリックがあります。一見の価値あり:)

C++ での自己登録オブジェクト -> http://www.ddj.com/184410633

編集

これが私が行ったテストアプリです(少し片付けました;):

object_factory.h

#include <string>
#include <vector>
// Forward declare the base object class
class Object;
// Interface that the factory uses to communicate with the object proxies
class IObjectProxy {
public:
    virtual Object* CreateObject() = 0;
    virtual std::string GetObjectInfo() = 0;
};
// Object factory, retrieves object info from the global proxy objects
class ObjectFactory {
public:
    static ObjectFactory& Instance() {
        static ObjectFactory instance;
        return instance;
    }
    // proxies add themselves to the factory here 
    void AddObject(IObjectProxy* object) {
        objects_.push_back(object);
    }
    size_t NumberOfObjects() {
        return objects_.size();
    }
    Object* CreateObject(size_t index) {
        return objects_[index]->CreateObject();
    }
    std::string GetObjectInfo(size_t index) {
        return objects_[index]->GetObjectInfo();
    }

private:
    std::vector<IObjectProxy*> objects_;
};

// This is the factory proxy template class
template<typename T>
class ObjectProxy : public IObjectProxy {
public:
    ObjectProxy() {
        ObjectFactory::Instance().AddObject(this);
    }        
    Object* CreateObject() {
        return new T;
    }
    virtual std::string GetObjectInfo() {
        return T::TalkToMe();
    };    
};

オブジェクト.h

#include <iostream>
#include "object_factory.h"
// Base object class
class Object {
public:
    virtual ~Object() {}
};
class ClassA : public Object {
public:
    ClassA() { std::cout << "ClassA Constructor" << std::endl; }
    ~ClassA() { std::cout << "ClassA Destructor" << std::endl; }
    static std::string TalkToMe() { return "This is ClassA"; }
};
class ClassB : public Object {
public:
    ClassB() { std::cout << "ClassB Constructor" << std::endl; }
    ~ClassB() { std::cout << "ClassB Destructor" << std::endl; }
    static std::string TalkToMe() { return "This is ClassB"; }
};

オブジェクト.cpp

#include "objects.h"
// Objects get registered here
ObjectProxy<ClassA> gClassAProxy;
ObjectProxy<ClassB> gClassBProxy;

main.cpp

#include "objects.h"
int main (int argc, char * const argv[]) {
    ObjectFactory& factory = ObjectFactory::Instance();
    for (int i = 0; i < factory.NumberOfObjects(); ++i) {
        std::cout << factory.GetObjectInfo(i) << std::endl;
        Object* object = factory.CreateObject(i);
        delete object;
    }
    return 0;
}

出力:

This is ClassA
ClassA Constructor
ClassA Destructor
This is ClassB
ClassB Constructor
ClassB Destructor
于 2009-08-11T16:34:56.743 に答える
1

Windows で C++/CLI を使用している場合、これはかなり簡単になります。.NET フレームワークは、リフレクションを介してこの機能を提供し、マネージ コードで非常にきれいに動作します。

ネイティブ C++ では、ライブラリやアプリケーションにランタイム情報を照会する簡単な方法がないため、これは少し複雑になります。これを提供するフレームワークはたくさんありますが(IoC、DI、またはプラグイン フレームワークを探してください)、自分で行う最も簡単な方法は、ファクトリ メソッドが自分自身を登録するために使用できる何らかの形式の構成を用意し、実装を返すことです。あなたの特定の基本クラス。DLL のロードとファクトリ メソッドの登録を実装するだけで済みます。これがあれば、かなり簡単です。

于 2009-08-11T15:17:29.347 に答える
1

あなたが考えることができるのは、オブジェクトカウンターです。この方法では、割り当てたすべての場所を変更する必要はなく、実装定義だけを変更する必要があります。これは、ファクトリ ソリューションの代替手段です。長所/短所を検討してください。

これを行うエレガントな方法は、CRTP: Curiously recurring テンプレート パターンを使用することです。主な例はそのようなカウンターです:)

このようにして、具体的なクラスの実装を追加するだけです:

class X; // your interface

class MyConcreteX : public counter<X>
{
    // whatever
};

もちろん、習得していない外部実装を使用する場合は適用されません。

編集:

正確な問題を処理するには、最初のインスタンスのみをカウントするカウンターが必要です。

私の2セント

于 2009-08-11T15:48:17.413 に答える
0

(ネイティブ) C++ のクラスのサブクラスを照会する方法はありません。インスタンスはどのように作成しますか? 作業しているすべてのサブクラスを反復処理できるようにするファクトリ メソッドの使用を検討してください。このようにインスタンスを作成すると、後で新しいサブクラスを追加することを忘れることはできません。

于 2009-08-11T15:22:23.037 に答える