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

Posted

技术标签:

【中文标题】使用工厂模式按名称实例化类【英文标题】:Instantiating classes by name with factory pattern 【发布时间】:2009-12-02 10:27:03 【问题描述】:

假设我有一个类 A, B, C, ... 的列表,它们都继承自 Base

我从用户那里得到类名作为字符串,我想实例化正确的类并返回一个指向Base 的指针。你将如何实现它?

我想过使用一个以类名作为键的哈希表,以及一个指向实例化正确类并返回Base *的函数的函数指针。

不过,我想我可能可以在这里使用工厂模式并使其变得更容易,但我记不太清了,所以我想请教一下。

【问题讨论】:

对我来说,您使用带有函数指针的哈希表的想法听起来很像工厂。 【参考方案1】:

这是一个通用的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;
;

这并不意味着在所有情况下都是最好的,但它旨在作为第一个近似值和比手动实现提到的函数 stijn 类型更有用的默认值。 Factory 并没有规定每个层次结构应该如何注册自己,但您可能会喜欢提到的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;

【讨论】:

为什么将其定义为 struct ?有什么原因吗?【参考方案2】:

在很多领域中最快但非常有用的方法,就像

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】:

您还可以查看 Boost 类 factory 的实现。

如果只有少数派生类,您可以使用“if, else”列表。 如果您计划拥有许多派生类,最好整理类注册过程(如Georg 所述),而不是使用“if, else”列表。

这是一个使用 Boost 工厂方法和类注册的简单示例:

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"]());

【讨论】:

嗨,我试过这个解决方案,但无法编译。我遇到的问题是在factories["Child1"] = boost::factory&lt;Child1*&gt;(); 行有一个类型检查问题,没有定义相等性。如果我写boost::factory&lt;Parent*&gt;(); 它可以工作,但它没有用。我错过了什么吗?【参考方案4】:

首先,是的,这正是工厂模式的用途。(顺便说一句,您的另一个想法是工厂模式的可能实现)

如果您打算为大型项目执行此操作(如果没有,请使用 stijns answer),您可能需要考虑在某处使用关联容器而不是显式分支,甚至可能移动 注册责任 到类中去

避免在另一个地方(您的工厂)更改代码 进而避免在添加类时可能需要很长的重新编译时间(对于in-header-implementations

为了方便在类中注册,您可以使用 this suggestion 之类的东西,并将函数指针或 functor 添加到实例化派生类并返回指向基类的指针的条目中。 如果您不害怕宏,您可以通过在工厂声明中添加一个小宏来向工厂添加类。

【讨论】:

以上是关于使用工厂模式按名称实例化类的主要内容,如果未能解决你的问题,请参考以下文章

简单工厂模式

工厂设计模式

快速理解工厂方法模式,及其在源码中的应用

快速理解工厂方法模式,及其在源码中的应用

工厂模式

工厂模式