繁体   English   中英

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

[英]Instantiating classes by name with factory pattern

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

我从类用户获取类名作为字符串,我想实例化正确的类并返回指向Base的指针。 你会如何实现这个?

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

但是,我想我可以在这里使用工厂模式并使其更容易,但我不能很好地记住它,所以我虽然我会寻求建议。

这是一个通用的工厂示例实现:

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

这并不意味着在每种情况下都是最好的,但它的目的是作为第一个近似和一个比手动实现所提到的函数类型更有用的默认值。 每个层次结构应该如何注册本身并不是Factory规定的,但是你可能会喜欢上面提到的方法 (它简单,清晰,非常有用,是的,这可以克服宏在这种情况下的固有问题)。

这是工厂的一个简单示例

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

在许多领域中最快捷但非常有用的方式就像是

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

您还可以查看Boost类工厂实现。

  • 如果只有少数派生类,则可以使用“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"]());

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

如果你打算为一个大项目做这个(如果没有,只需要使用stijns答案 ),你可能想考虑在某处使用关联容器而不是显式分支,甚至可能将注册责任移到类中

  • 避免在一个额外的地方(您的工厂)更改代码
  • 并且在添加类时反过来避免可能很长的重新编译时间(对于in-header-implementation

为了在类中实现方便的注册,您可以使用类似于此建议的内容,并向实例化派生类的条目添加函数指针或函子 ,并返回指向基类的指针。
如果您不害怕宏,则可以通过在其声明中添加一个小宏来向工厂添加类。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM