我們創建一個對象時,必須給出具體型別,new A也好,new B也罷。A,B都是對象的型別。我們向來反對寫hardcode,但卻在創建對象時必須hardcode。
如果需要根據用戶的輸入信息,或是網絡反饋信息,或是文本文件信息來創建對象時,應該怎麼辦呢?
最初我們想法可能是這樣的,偽代碼
switch(Info)
{
case a: return new A;
case b: return new B;
...
default:...
}
這就是大家都熟悉簡單工廠模式,他雖然簡單,但蘊含一個基本原則:根據value(a,b...)查找type(A,B...),利用type創建value(A*,B*...)。
利用對象工廠,我們創建對象時得到了解脫,無需寫hardcode,不必給出類型,我們可以提供數字,字符串,以及特定格式信息來創建一個對象。
好的,這就是對象工廠存在的意義!
是的,不夠完美。理由有三:
1 struct package
2 {
3 void * funcSet;
4 void * func;
5 size_t index;
6 string sig;
7 };
8
9 template <typename ...> class TypeList {};
10
11 template <typename AbstractProduct ,typename IdentifierType = string> class FactoryImpl
12 {
13 public:
14 template <typename... Arg> bool Register(const IdentifierType& id,const function<unique_ptr<AbstractProduct>(Arg...)>& creator)
15 {
16
17 static vector<function<unique_ptr<AbstractProduct>(Arg...)>> vf;
18 typename AssocMap::const_iterator i =associations_.find(id);
19 if(i!= associations_.end()) return false;
20 vf.push_back(creator);
21 return associations_.insert(typename AssocMap::value_type(id,package {&vf,&vf.back(),vf.size()-1,string(typeid(TypeList<Arg...>).name())})).second;
22
23 }
24
25 template <typename ... Arg >
26 bool UnRegister(const IdentifierType& id)
27 {
28 typename AssocMap::const_iterator i =associations_.find(id);
29 if(i != associations_.end())
30 {
31 assert(
32 ((i->second).sig).compare(typeid(TypeList<Arg...>).name())==0
33 );
34 auto vf=static_cast<vector<function<unique_ptr<AbstractProduct>(Arg...)>>*>((i->second).funcSet);
35 vf->erase(vf->begin()+(i->second).index);
36 }
37
38 return associations_.erase(id)==1;
39 }
40
41 template <typename... Arg> unique_ptr<AbstractProduct> Createobject(const IdentifierType& id,Arg&&... args)
42 {
43 typename AssocMap::const_iterator i =associations_.find(id);
44
45 if(i != associations_.end())
46 {
47 assert(((i->second).sig).compare(typeid(TypeList<Arg...>).name())==0);
48 auto funp=static_cast<function<unique_ptr<AbstractProduct>(Arg...)>* >((i->second).func);
49 return (*funp)(std::forward<Arg>(args)...);
50 }
51 assert(false);
52 }
53
54
55 private:
56 typedef std::unordered_map<IdentifierType,package> AssocMap;
57 AssocMap associations_;
58
59 };
代碼釋疑:
因為沒有在類模板使用可變參數,所以實現起來很是費勁,我現在倒覺得可以使用類模板帶可變參數實現。這樣類模板實例化的類,雖然只支持注冊和調用參數固定調用體,但具有編譯期類型檢查的好處,Register和UnRegister均很容易實現。用戶需要時,根據需要實例化模板即可。
template <typename AbstractProduct ,typename IdentifierType ,typename... Arg > class Factory
{
public:
bool Register(const IdentifierType& id, std::function<unique_ptr<AbstractProduct> (Arg...)> creator)
{
return associations_.insert(typename AssocMap::value_type(id,creator)).second;
}
bool UnRegister(const IdentifierType& id)
{
return associations_.erase(id)==1;
}
unique_ptr<AbstractProduct> Createobject(const IdentifierType& id,Arg&&... args)
{
typename AssocMap::const_iterator i =associations_.find(id);
if(i != associations_.end())
{
return (i->second)(std::forward<Arg>(args)...);
}
assert(false);
}
private:
typedef std::unordered_map<IdentifierType,std::function<unique_ptr<AbstractProduct> (Arg...)> > AssocMap;
AssocMap associations_;
};
需要注意一下,這代碼因為使用變長模板參數,所以無法使用默認模板參數。
使用第一種實現方式,你只需要一個對象,即可搞定所有需求(UnRegister比較鬧心)。具有運行期類型檢查。
使用第二種實現方式,你要在不同場合下,實例化出類,並且創建出對象只支持注冊某種調用格式調用體。而且具有編譯期類型檢查。
雖然在第一種方式上,下了很多功夫,但是我還是不推薦這種做法,除非真的有萬不得已需要,非要把代碼設計成這樣。
春節前,最後一篇文章,轉載請標明出處,謝謝。