インターフェイスを実装するすべてのクラスを追跡(列挙)するにはどうすればよいですか
質問
プログラム内で特定の役割を果たすために、特定のクラスがどのように動作するかを定義するインターフェースがあるという状況がありますが、現時点では、その役割を果たすためにいくつのクラスを作成するかが 100% わかりません。 。ただし、同時に、ユーザーが特定の役割を果たすために使用するインターフェイスを実装する具体的なクラスを GUI コンボ/リスト ボックスから選択できるようにしたいこともわかっています。GUI で使用可能なすべてのクラスを列挙できるようにしたいのですが、その役割を果たす新しいクラスを実装することにしたとき(数か月後になる可能性があります)、古いコードに戻って変更する必要はありません。
私が検討したいくつかのこと:
- 列挙型を使用する
- 長所:
- やり方は知っています
- 短所
- 新しいクラスを追加するときに列挙を更新する必要があります
- 反復処理するには醜い
- 長所:
- ある種のものを使用して
static
インターフェイス内のリスト オブジェクト、および実装クラスの定義ファイル内から新しい要素を追加する- 長所:
- 古いコードを変更する必要はありません
- 短所:
- それが可能かどうかさえわかりません
- ファクトリ メソッドが適切なコンストラクタ (おそらく文字列と、インターフェイスのオブジェクトへのポインタを返す関数ポインタの間のマップ) を選択できるように、どのような種類の情報を保存すればよいかわかりません。
- 長所:
これは、経験豊富なプログラマがおそらく以前に (そして頻繁に) 遭遇したことがある問題 (または問題に似た問題) であり、この種の問題にはおそらく共通の解決策があり、それはほぼ間違いなく私が考えた解決策よりも優れています。思いつくことができるよ。それで、どうすればいいでしょうか?
(追記)検索しましたが、見つかったのはこれだけで、同じではありませんでした。 汎用インターフェイスを実装するすべての項目を列挙するにはどうすればよいですか?. 。彼は私が理解しようとしている問題の解決方法をすでに知っているようです。)
編集:タイトルを「どうすれば追跡できますか?」に変更しました。なぜなら、元の質問は、私が実際に興味があるのはコンパイル時の簿記であるのに対し、ランタイム環境の調査にもっと興味があるように聞こえたからです。
解決
あなたはクリエータ関数へのポインタを使用してクラスを登録することができシングルトンを作成します。
具象クラスの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()
{
//....
}
他のヒント
私は漠然と、この何年も前に似た何かを覚えています。あなたのオプション(2)は、ほとんど私がやったことです。その場合、std::map
にstd::string
のstd::typeinfo
ました。それぞれ、.cppファイルでは私はこのようなクラスを登録します:
static dummy = registerClass (typeid (MyNewClass));
registerClass
はtype_info
オブジェクトを受け取り、単にtrue
を返します。あなたはregisterClass
が起動時間中に呼び出されることを保証する変数を初期化する必要があります。単にグローバル名前空間にregisterClass
を呼び出すとエラーになります。そして作るdummy
静的では、名前の衝突せずにコンパイル単位全体で名を再利用することができます。
私は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();
};
};
objects.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"; }
};
objects.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
あなたが考えることができます何かがオブジェクトカウンタです。この方法は、あなたは、すべてあなたが割り当て場所が、ちょうど実装の定義を変更する必要はありません。これは、工場出荷時のソリューションに代わるものです。長所/短所を考えてみます。
不思議なことに、定期的なテンプレートパターンで:それを行うためのエレガントな方法は、 CRTPを使用することです。 主な例は、このようなカウンタである。)
この方法であなたは自分の具象クラスの実装に追加する必要があります:
class X; // your interface
class MyConcreteX : public counter<X>
{
// whatever
};
あなたが習得していない外部の実装を使用する場合は、もちろん、それが適用されていません。
EDITます:
あなたが最初のインスタンスのみをカウントするカウンタを持っている必要があり、正確な問題に対処するために。
私の2セント。
(ネイティブ)C ++でクラスのサブクラスを照会する方法はありません。 どのようにしてインスタンスを作成するのですか?あなたが作業しているすべてのサブクラスを反復処理できるようにするファクトリメソッドを使用することを検討してください。あなたはこのようなインスタンスを作成すると、後に新しいサブクラスを追加することを忘れすることはできません。