2009-08-11 94 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

    相关问题