2016-03-15 33 views
4

我用下面(簡化)的工廠設計創造出一些繼承層次的對象時,不應該是什麼特別的東西:如何在工廠設計中支持多個建築簽名?

// class to create 
class Class 
{ 
public: 
    Class(Type type, Foo foo); 
}; 

// Simple creator class. 
// Used in practice to do some runtime checks about whether or not construction is allowed. 
class Creator 
{ 
public: 
    Class* create(Type type, Foo foo) const 
    { 
    return new Class(type, foo); 
    } 
}; 

class Factory 
{ 
public: 
    Factory 
    { 
    // fill object creator map on construction 
    _map[ "name" ] = new Creator<Class>; 
    } 

    Class* create(const std::string& name, Type type, Foo foo) 
    { 
    // fowards to map entry 
    return _map[name]->create(type, foo); 
    } 

private: 
std::map<std::string, Creator*> _map; 
} 

// client code 
int main() 
{ 
    Factory f; 
    factory.create(name, type, foo); 
} 

現在我遇到的問題,一旦我要創造出有一個子類不同的構造函數簽名,因爲工廠在整個繼承層次上施加了一個固定的簽名。即對於下面的類,我沒有辦法通過工廠構造來指定新的第三個參數,而不需要再次將這個擴展簽名強加給我的所有其他類。

class ExtClass : public Class 
{ 
public: 
    Class(Type type, Foo foo, NewMember nm) 
    : Class(type, foo), 
     _nm(nm) 

private: 
    NewMember _nm; 
}; 

有沒有一種方法可以使我的當前設計的工作沒有做出pricinpal更改?我正在考慮使用模板或綁定對象來進行不同的參數調用。 或者你會在這種情況下提出一個不同於工廠設計的解決方案嗎?

+0

如何重載'創建()'? – CinCout

+0

也許這有助於:http://stackoverflow.com/questions/32200981/bind-make-shared-with-variadic-template – ChronoTrigger

+0

它有助於擴大我的觀點,但不完全適用於我的問題。在此線程中,參數標誌在工廠對象上模板化。在我的情況下,它完全被抽象出來,理想情況下應該從create方法的簽名中推導出來。重載創建是一個解決方案,但對於50個不同的簽名,我需要50個不同的重載。如果只支持這些虛擬繼承,這確實會要求可變參數模板。 – user1709708

回答

0

不同的命名傳遞道歉,但這是我目前使用的C++ 14解決方案。主叫CreateObject當兩個主要缺點是

  1. ,作爲 參數傳遞的值的類型必須是相同註冊類型。您無法通過 a float並調用註冊爲double 簽名的構造函數。
  2. 由於boost::bind中的實現細節, 參數必須爲const &

的設計限制,因爲我想用boost::factory是該類的對象必須被包裹在一個boost::function(消除歧義函數簽名)。

所以它的工作原理,但它絕對可以與更多的元編程的智慧改進:

#include <boost/functional/factory.hpp> 
#include <boost/function.hpp> 
#include <boost/bind.hpp> 

#include <cassert> 
#include <map> 
#include <tuple> 
#include <type_traits> 
#include <utility> 


template <class AbstractProduct, typename IdentifierType, typename... ProductCreators> 
class Factory 
{ 
    using AssociativeContainers = std::tuple<std::map<IdentifierType, boost::function<ProductCreators>>...>; 
public: 
    template <typename Product, typename... Arguments> 
    bool Register(const IdentifierType& id, boost::function<Product(Arguments...)> creator) { 
     auto &foo = std::get<std::map<IdentifierType, boost::function<AbstractProduct(const Arguments&...)>>>(associations_); 
     return foo.emplace(id, creator).second; 
    } 

    // This function left as an exercise to the reader... 
    bool Unregister(const IdentifierType& id) { 
     return associations_.erase(id) == 1; 
    } 

    template <typename... Arguments> 
    AbstractProduct CreateObject(const IdentifierType& id, Arguments&& ... args) const { 
     auto const &foo = std::get<std::map<IdentifierType, boost::function<AbstractProduct(const Arguments&...)>>>(associations_); 
     auto const i = foo.find(id); 
     if (i != foo.end()) { 
      return (i->second)(std::forward<Arguments...>(args)...); 
     } 
     throw std::runtime_error("Creator not found."); 
    } 

private: 
    AssociativeContainers associations_; 
}; 


struct Arity { 
    virtual ~Arity() = default; 
}; 

struct Nullary : Arity {}; 

struct Unary : Arity { 
    Unary() {} 
    Unary(double x) : x(x) {} 

    double x; 
}; 


int main(void) 
{ 
    Factory<Arity*, int, Arity*(), Arity*(const double&)> factory; 
    factory.Register(0, boost::function<Arity*()>{boost::factory<Nullary*>()}); 
    factory.Register(1, boost::function<Arity*(const double&)>{boost::bind(boost::factory<Unary*>(), _1)}); 
    auto x = factory.CreateObject(1, 2.0); 
    assert(typeid(*x) == typeid(Unary)); 
    x = factory.CreateObject(0); 
    assert(typeid(*x) == typeid(Nullary)); 
}