2009-08-11 91 views
6

我有一種情況,我有一個接口來定義某個類的行爲,以便填充程序中的某個角色,但此時我不是100%確定我會寫多少課來填補這個角色。然而,與此同時,我知道我希望用戶能夠從GUI組合/列表框中選擇實現他們想要用來填充某個角色的接口的具體類。我希望GUI能夠枚舉所有可用的類,但是我希望在我決定實現新類時填充該角色(可能從現在開始幾個月)不必返回並更改舊代碼我該如何跟蹤(枚舉)實現接口的所有類

有些事情我已經考慮:使用枚舉

  • 優點

      1. 我知道如何做到這一點
    1. 缺點
      1. 我將有更新的時候我添加一個新類
      2. 醜陋使用某種static列表對象的接口,通過
  • 迭代更新枚舉,並從執行類的定義文件中添加新元素
    • 優點:
      1. 不會必須改變舊的代碼
    • 缺點:
      1. 甚至不知道這是可能的
      2. 不知道存儲什麼樣的信息,以便工廠方法可以選擇正確的構造函數(可能是字符串和函數指針之間的映射,它返回指向接口對象的指針)
  • 我猜這是一個問題(或類似於一個問題),更有經驗的程序員可能會遇到過(經常),並可能有一個共同的解決方案,這種問題,這是幾乎肯定比我能夠提出的任何東西都要好。那麼,我該怎麼做呢?

    (PS我搜索,但所有我發現了這一點,它是不一樣的:How do I enumerate all items that implement a generic interface?看樣子他已經知道怎麼解決,我想弄清楚這個問題。)

    編輯:將標題重命名爲「我怎樣才能跟蹤...」,而不是「我怎樣枚舉......」,因爲原來的問題聽起來像我更感興趣的是檢查運行時環境,因爲我真的感興趣的是編譯時簿記。

    +0

    你說的接口是什麼意思?一個真正的接口類或該類的特定方法? – TimW 2009-08-11 15:11:46

    +0

    一個真正的接口類:定義與該接口的一個對象交互的各種方法的集合 – cheshirekow 2009-08-11 16:12:34

    回答

    6

    創建一個單例,您可以在其中使用指向創建者函數的指針註冊您的類。 在具體類的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() 
    { 
        //.... 
    } 
    
    +1

    而且您可以將註冊包裝在宏中以使其更易於使用: REGISTER_CLASS(ClassY) – 2009-08-11 15:45:15

    +1

    謝謝,該代碼段非常有用。是否有任何特別的理由使用這個額外的類,而不是將地圖存儲爲接口的靜態成員。據我所知,它提供了一個合理的「create()」方法,但是其他原因。這可能作爲一個通用的單例特別有用,然後我可以爲我知道將在未來擴展的每個接口實例化一個接口。 – cheshirekow 2009-08-11 15:53:43

    +0

    +1爲編碼工作:) – neuro 2009-08-11 16:30:45

    1

    如果您使用的是Windows,並且使用C++/CLI,則這變得相當容易。這個。.NET框架通過反射來提供這種功能,並且它在託管代碼中非常乾淨地工作。

    在原生C++中,這有點棘手,因爲沒有簡單的方法來查詢庫或應用程序的運行時信息。有很多frameworks that provide this(只需查找IoC,DI或插件框架),但最簡單的做法是使用某種形式的配置,工廠方法可以使用這些配置來註冊自己,並返回特定基礎的實現類。你只需要實現加載一個DLL,並註冊工廠方法 - 一旦你有了,這很容易。

    +0

    我在windows上,但是,唉,該程序也需要在Linux上運行(所以我使用GNU編譯器)。我不需要去查詢運行時間。實際上,我會更喜歡編譯時(或更確切地說是鏈接時間)解決方案。 – cheshirekow 2009-08-11 15:35:45

    0

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

    +0

    這正是我想要做的,但我想要一個乾淨的方式來做記錄,所以每次我編寫新的子類時,我都不必更新該工廠方法的代碼。 – cheshirekow 2009-08-11 15:37:10

    3

    我依稀記得做了類似於很多年前的事情。你的選擇(2)幾乎是我所做的。在那種情況下,它是std::stringstd::typeinfostd::map。在每一個,.cpp文件我註冊的類是這樣的:

    static dummy = registerClass (typeid (MyNewClass)); 
    

    registerClass需要type_info對象,只是返回true。您必須初始化變量以確保在啓動期間調用registerClass。在全局名稱空間中僅調用registerClass是一個錯誤。並且使dummy靜態允許您在沒有名稱衝突的編譯單元中重複使用該名稱。

    +0

    好吧,聽起來不錯。我對靜態初始化的知識並不是100%自信,但如果這是一個可行的計劃,我會開始嘗試。 typeid操作符會很有幫助。我會回來,讓這個答案,如果我得到它的工作。 – cheshirekow 2009-08-11 15:45:37

    +0

    重讀答案我認爲這個解決方案是最好的(現在?)。不需要用工廠方法混淆代碼,它可以與現有的代碼一起工作,而且我提出的更直接。由於C++沒有太多的RTTI機制,我認爲你可以做得更好...... +1人。 – neuro 2009-08-11 16:26:44

    +0

    你如何創建一個註冊類? – TimW 2009-08-11 17:49:05

    1

    有些事情,你可以考慮的是一個對象計數器。這樣你就不需要改變你分配的每個地方,而只需要實現定義。這是工廠解決方案的替代方案。考慮利弊。

    一個優雅的方法是使用CRTP : Curiously recurring template pattern。 主要的例子是這樣的計數器:)

    這種方式,你只需要在你的具體類實現增加:

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

    當然,這是不適用的,如果你使用你沒有掌握外部實現。

    編輯:

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

    我的2美分

    +0

    這是一個非常聰明的方法來解決這個問題。我假設計數器擴展了X幷包含一個靜態對象計數,在構造時遞增並在銷燬時遞減。我認爲這有點間接,但我需要的不是實際的對象數,而是枚舉可用子類的一種方法。 – cheshirekow 2009-08-11 16:10:14

    +0

    不完全是這樣,它使用了值得一看的CRTP模式。請參閱我的答案中的鏈接,因爲提供了此類計數器的代碼。我的答案中的想法是使用一個對象計數器,它只計算第一個實例。我知道有需要實例化至少一個對象(儘管可以是靜態的虛擬對象),但似乎比工廠方法更好。 – neuro 2009-08-11 16:20:20

    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(); 
        };  
    }; 
    

    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 
    
    +0

    但是,然後註冊是隱含的,你需要創建每個具體類的實例。我更喜歡沒有創建對象的顯式註冊。 – TimW 2009-08-11 17:35:07

    +0

    您爲每個具體類創建了一個工廠代理,但這些類本身僅在需要時通過工廠創建。當我今晚回家時,我會用一些代碼編輯答案:) – irh 2009-08-11 18:57:22

    +0

    感謝您的澄清 – TimW 2009-08-12 09:34:49

    相關問題