代码之家  ›  专栏  ›  技术社区  ›  Idan

用工厂模式按名称实例化类

  •  4
  • Idan  · 技术社区  · 15 年前

    假如我有一张课程表 A, B, C, ... 它们都继承自 Base .

    我从用户那里得到一个字符串形式的类名,我想实例化正确的类并返回一个指向 基地 . 您将如何实现这一点?

    我考虑使用一个以类名为键的哈希表,以及一个指向函数的函数指针,该函数实例化了正确的类并返回 Base * .

    不过,我想我可以在这里使用工厂模式,使它更容易,但我只是不太清楚它,所以我想我会征求建议。

    4 回复  |  直到 14 年前
        1
  •  7
  •   Community kfsone    7 年前

    这是一个通用的 factory example 实施:

    template<class Interface, class KeyT=std::string>
    struct Factory {
        typedef KeyT Key;
        typedef std::auto_ptr<Interface> Type;
        typedef Type (*Creator)();
    
        bool define(Key const& key, Creator v) {
            // Define key -> v relationship, return whether this is a new key.
            return _registry.insert(typename Registry::value_type(key, v)).second;
        }
        Type create(Key const& key) {
            typename Registry::const_iterator i = _registry.find(key);
            if (i == _registry.end()) {
                throw std::invalid_argument(std::string(__PRETTY_FUNCTION__) +
                                            ": key not registered");
            }
            else return i->second();
        }
    
        template<class Base, class Actual>
        static
        std::auto_ptr<Base> create_func() {
            return std::auto_ptr<Base>(new Actual());
        }
    
    private:
        typedef std::map<Key, Creator> Registry;
        Registry _registry;
    };
    

    这并不意味着在每种情况下都是最好的,但它的目的是作为一种近似值和一种比手动实现上述函数类型更有用的默认值。每个层次结构应该如何注册自己并不是由工厂强制的,但是您可能喜欢 method gf提到了(它简单、清晰、非常有用,是的,在这种情况下,它克服了宏的固有问题)。

    这是一个 simple example 工厂的:

    struct Base {
        typedef ::Factory<Base> Factory;
        virtual ~Base() {}
        virtual int answer() const = 0;
    
        static Factory::Type create(Factory::Key const& name) {
            return _factory.create(name);
        }
        template<class Derived>
        static void define(Factory::Key const& name) {
            bool new_key = _factory.define(name,
                &Factory::template create_func<Base, Derived>);
            if (not new_key) {
                throw std::logic_error(std::string(__PRETTY_FUNCTION__) +
                                       ": name already registered");
            }
        }
    
    private:
        static Factory _factory;
    };
    Base::Factory Base::_factory;
    
    struct A : Base {
        virtual int answer() const { return 42; }
    };
    
    int main() {
        Base::define<A>("A");
        assert(Base::create("A")->answer() == 42);
        return 0;
    }
    
        2
  •  3
  •   stijn    15 年前

    在许多领域中,最快也是最有用的方法是

    Base* MyFactoryMethod( const std::string& sClass ) const
    {
      if( sClass == "A" )
        return CreateNewA();
      else if( sClass == "B" )
        return new CreateClassB();
      //....
      return 0;
    }
    
    A* CreateClassA() const
    {
      return new A();
    }
    
        3
  •  2
  •   Community kfsone    7 年前

    你也可以看一下Boost课程 factory 实施。

    • 如果只有几个派生类,则可以使用“if,else”列表。
    • 如果您计划有许多派生类,最好对类注册过程进行分类(如 Georg 提及)而不是使用“if,else”列表。

    下面是一个使用BoostFactory方法和类注册的简单示例:

    typedef boost::function<Parent*()> factory;
    
    // ...
    
    std::map<std::string, factory> factories;
    
    // Register derived classes
    factories["Child1"] = boost::factory<Child1*>();
    factories["Child2"] = boost::factory<Child2*>();
    
    // ...
    
    // Instantiate chosen derived class
    auto_ptr<Parent> pChild = auto_ptr<Parent>(factories["Child1"]());
    
        4
  •  1
  •   Community kfsone    7 年前

    首先,是的,这正是工厂模式的目的。
    (顺便说一下,您的另一个想法是实现工厂模式)

    如果你打算为一个大项目做这件事(如果不是的话,只要去 stijns answer ,您可能需要考虑在某个地方使用关联容器,而不是显式分支,甚至可以移动 登记责任 到班级里去

    • 避免在另一个地方(您的工厂)更改代码
    • 反过来,避免可能很长的重新编译时间(对于 在头实现中 )添加类时

    为了在课堂上实现方便的注册,你可以使用 this suggestion 并添加一个函数指针或 functor 指向实例化派生类并返回指向基的指针的项。
    如果您不害怕宏,那么您可以通过在声明中添加一个小宏来向工厂添加类。