2014-01-28 65 views
6

我想創造這樣一個普遍的工廠方法 - 看看這一個:問題與通用工廠方法和可變參數模板

template <class BaseType> 
class Factory { 
    public: 
    template <class ... Args> 
    static BaseType* Create(const Args& ... args) { 
     return new DerivedType(args ...); 
    } 
}; 

DerivedTypeBaseType衍生和定義在一些其他類型不同的地方。

問題是存儲DerivedType。我想這樣做,例如,像這樣:

void f() { 
    // Derived type may have more than one constructor, 
    // that's why I suggest using of the variadic templates. 
    BaseType* ptr1 = Factory<BaseType>::Create("abc", 5, 10.); 
    BaseType* ptr2 = Factory<BaseType>::Create(); 
    ... 
} 

... 

Factory<BaseType>::SetType<MyDerivedType>(); 
f(); 

Factory<BaseType>::SetType<YourDerivedType>(); 
f(); 

我可以設置不同的派生類型,但所有的人都在編譯時已知的。 我想不出一個合適的技術來做到這一點。

問題:你能建議嗎?


這樣做的基本原理(因此,原來的問題,如果有人提出的問題是它自身的XY問題) - 是一個能力單元測試的代碼中的一些棘手的部分。舉例來說,如果我有一個代碼:

... 
Shuttle* shuttle1 = new ShuttleImpl("Discovery", Destination::Moon); 
Shuttle* shuttle2 = new ShuttleImpl(); 
... 

而且我不想每次真正構建接送我運行單元測試:

class Shuttle: public Factory<Shuttle> { ... } 
... 
Shuttle* shuttle1 = Shuttle::Create("Discovery", Destination::Moon); 
Shuttle* shuttle2 = Shuttle::Create(); 
... 

所以,在單元測試我可以這樣做:Shuttle::SetType<TestShuttle>();

可能有更多的「可測試的」類,這就是爲什麼我需要一個通用的工廠所有的人:

class Car: public Factory<Car> { ... } 
class Driver: public Factory<Driver> { ... } 
... 
+0

我猜某種形式的類型擦除和模仿的'std'分配器的接口將是探索的方向,但不知道是否它實際上是可能的。 – Angew

+0

爲什麼你不能使用第二個模板參數,例如:'template class Factory;'? – CouchDeveloper

+0

@CouchDeveloper - 因爲在代碼中可以有多個「Derived」:即我可以用一個完整的模擬或者一個特殊的測試類來測試相同的代碼,以檢查方法調用的數量。 –

回答

0

不是一個完整的答案,但你的類模板的Create靜態函數模板應該是:

template <class BaseType> 
class Factory { 
    public: 
    template <class... Args> 
    static BaseType* Create(Args&&... args) { 
     return new DerivedType(std::forward<Args>(args)...); 
    } 
}; 

又見When to use std::forward to forward arguments?


編輯:

爲什麼第二個模板參數不能解決您的問題?

例如:

template <class Base, class Derived> 
class Factory { 
    public: 
    template <class... Args> 
    static Base* Create(Args&&... args) { 
     return new Derived(std::forward<Args>(args)...); 
    } 
}; 

而不是

Factory<BaseType>::SetType<MyDerivedType>(); 
f(); 

Factory<BaseType>::SetType<YourDerivedType>(); 
f(); 

你可以寫:

Factory<MyBase, MyDerived1> factory1; 
Factory<MyBase, MyDerived2> factory2; 

auto object1 = factory1::Create(1, "a"); 
auto object2 = factory2::Create(1.2, "abc"); 
+0

第二個模板參數不能解決問題,因爲我*必須*不會將原始調用更改爲'Create()'。即我的代碼中有函數'f()'代表的代碼,這是不可變的,我有單元測試,它必須改變'f()'中的一些對象。否則,如果必須將原始代碼更改爲具有不同'Derived'的每個調用,例如'f1()','f2()'等,我的示例代碼很平凡,而真正的程序是更復雜。 –

+0

此外,在你的答案中的'object1'和'object2'應該一次使用相同的派生類型,就我的示例代碼而言。 –

+0

@death.7你應該認識到,如果你不能改變已經從模板創建的'Create'的代碼 - 那麼就沒有辦法注入模擬。你必須在運行時做這樣的事情。但是,這需要完全不同的設計。 – CouchDeveloper

0

如果你的工廠知道所有可能的派生類,下面可以幫助:

// get_index<T, T1, .., TK-1, T, Ts...> is std::integral_constant<std::size_t, K> 
template <typename T, typename ... Ts> struct get_index; 

template <typename T, typename ... Ts> 
struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {}; 

template <typename T, typename Tail, typename ... Ts> 
struct get_index<T, Tail, Ts...> : 
     std::integral_constant < std::size_t, 1 + get_index<T, Ts...>::value > {}; 

template <typename Base, typename...Deriveds> 
struct Factory 
{ 
private: 
    template <typename Derivated, typename...Ts> 
    static constexpr Base* allocator(Ts&&...args) 
    { 
     return new Derivated(std::forward<Ts>(args)...); 
    } 

    template <typename...Ts> 
    static constexpr std::array<Base*(*)(Ts&&...), sizeof...(Deriveds)> 
    array_alloc() 
    { 
     return std::array<Base*(*)(Ts&&...), sizeof...(Deriveds)> 
      {{ &allocator<Deriveds, Ts&&...>... }}; 
    } 

public: 
    template <typename...Ts> 
    static Base* create(Ts&&...args) 
    { 
     return array_alloc<Ts...>()[active](std::forward<Ts>(args)...); 
    } 

    template <typename Derived> 
    static void setType() 
    { 
     active = get_index<Derived, Deriveds...>::value; 
    } 

private: 
    static std::size_t active; 
}; 

template <typename Base, typename...Deriveds> 
std::size_t Factory<Base, Deriveds...>::active = 0; 

而且使用它像:

class Base {}; 

struct D1 : Base { 
    D1() {std::cout << "D1" << std::endl;} 
    D1(int a, int b) {} 
}; 

struct D2 : Base { 
    D2() {} 
    D2(int a, int b) { std::cout << "D2(" << a << ", " << b << ")" << std::endl; } 
}; 

int main(int argc, char *argv[]) 
{ 
    typedef Factory<Base, D1, D2> BaseFactory; // default to D1 

    Base* b1 = BaseFactory::create(); // D1() 
    BaseFactory::setType<D2>(); 
    Base* b2 = BaseFactory::create(42, 53); // D2(42, 53) 

    delete b2; 
    delete b1; 

    return 0; 
}