簡體   English   中英

如何跟蹤(枚舉)實現接口的所有類

[英]How can I keep track of (enumerate) all classes that implement an interface

我有一種情況,我有一個界面,定義某個類的行為,以填補我的程序中的某個角色,但在這個時間點,我不是100%肯定我將編寫多少個類來填補該角色。 但是,與此同時,我知道我希望用戶能夠從GUI組合/列表框中選擇實現他們想要用來填充某個角色的界面的具體類。 我希望GUI能夠枚舉所有可用的類,但是每當我決定實現一個新類來填充該角色時,我寧願不必返回並更改舊代碼(這可能是幾個月之后)

我考慮過的一些事情:

  1. 使用枚舉
    • 優點:
      1. 我知道該怎么做
    • 缺點
      1. 添加新類時,我將不得不更新枚舉更新
      2. 難以迭代
  2. 在接口中使用某種static列表對象,並在實現類的定義文件中添加新元素
    • 優點:
      1. 不必更改舊代碼
    • 缺點:
      1. 甚至不確定這是否可行
      2. 不確定要存儲什么樣的信息,以便工廠方法可以選擇正確的構造函數(可能是字符串和返回指向接口對象的指針的函數指針之間的映射)

我猜這是一個問題(或類似的問題),更有經驗的程序員可能在之前(經常)遇到過,並且這種問題可能有一個共同的解決方案,這幾乎肯定比任何我都好。能夠想出來的。 那么,我該怎么做?

(PS我搜索過,但我發現的都是這個,而且它不一樣: 我如何枚舉所有實現通用接口的項目?看來他已經知道如何解決我想弄清楚的問題了。)

編輯:我將標題重命名為“我怎樣才能跟蹤......”,而不僅僅是“我怎么能枚舉......”因為原來的問題聽起來像是我對檢查運行時環境更感興趣,我在哪里真正感興趣的是編譯時簿記。

創建一個單例,您可以使用指向創建者函數的指針來注冊類。 在具體類的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::mapstd::stringstd::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

如果您使用的是Windows,並且使用的是C ++ / CLI,這就變得非常簡單了。 .NET框架通過反射提供此功能,並且在托管代碼中非常干凈地工作。

在本機C ++中,這有點棘手,因為沒有簡單的方法來查詢庫或應用程序的運行時信息。 有許多框架可以提供這個 (只需查看IoC,DI或插件框架),但最簡單的方法是自己做一些配置,工廠方法可以使用它來注冊自己,並返回一個實現你的特定基類。 你只需要實現加載DLL,並注冊工廠方法 - 一旦你有了它,它就相當容易了。

你可以考慮的是一個對象計數器。 這樣您就不需要更改分配的每個位置,只需更改實現定義。 它是工廠解決方案的替代品。 考慮利弊。

一種優雅的方法是使用CRTP:奇怪的重復模板模式 主要的例子是這樣一個櫃台:)

這樣你只需添加你的具體類實現:

class X; // your interface

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

當然,如果您使用未掌握的外部實現,則不適用。

編輯:

要處理確切的問題,您需要一個只計算第一個實例的計數器。

我的2美分

無法在(本機)C ++中查詢類的子類。 你如何創建實例? 考慮使用工廠方法,允許您迭代您正在使用的所有子類。 當您創建這樣的實例時,以后不可能忘記添加新的子類。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM