1 引子
這是一個幸福的時代,特別是作為一個java程序員(感慨一下,java程序員確實比C/C++程序員幸福)。
基于我個人的一貫風格,我不準備采用大量技術術語和官腔講述這次的主題:工廠模式應用與發展。今時今日提到工廠模式,相信絕大部分的人都已經對這個詞匯有所了解(恩恩,注意我的用詞,我沒說“對他的概念、含義和用途有所了解”)。
那么,在開始進入正題之前,我們先回顧一下工廠模式的作用(我不準備列舉在這,自己想想)。
OK,下面開始我的講述,請大家耐心點(是的,我怕有人半途而廢;噢,再啰嗦一下,重點在示例代碼)!
2 石器時代
2.1 痛苦的開始
據說原始人類所使用的工具的產生有很大的隨機性。例如這樣的情況:最開始大家吃肉肉,一窩蜂撲上去撕咬;后來可能覺得不衛生(也可能是覺得咬起來牙齒疼,也可能是覺得自己每次都咬不過別人),于是按自己牙齒的模樣,找些放大比例的動物牙齒(或者石頭),嘗試著割肉,發現挺好使的,于是工具產生了。
代碼界同樣有這樣的事情發生(產品的童鞋要我們為每個名人設計一句話):
...
if(strcmp(name, "bill gates") == 0) {
printf("Do you like Win8?");
} else if(strcmp(name, "Steve jobs") == 0) {
printf("so nice iPhone.");
} else if(strcmp(name, "Zuckerberg") == 0) {
printf("Welcome to facebook.");
} else {
...
看上去這樣沒有什么不妥,是不是?
然而,事情遠遠沒有這么簡單。
“花心”的產品童鞋總會給我們提點新要求,有木有?他們很可能覺得每個名人都應該充分體現自己的個性,要求為他們增加肢體語言什么的。有木有?
好吧,我們的痛苦從此開始了……
2.2 夢想
我猜想,原始人類估計不像我們現在這么貪婪,他們在尋找牙齒與石頭的時候,很可能是在想:老天爺,賜我一把割肉神器吧(而不是:老天爺,賜我一具不用吃飯的肉體吧)!當然,在得到神器的那一天來臨之前,我們的原始先祖們,還是不得不繼續尋找更鋒利的牙齒與石頭。
是的,代碼界也發生了一些變化:
...
void billgatesShowtime() {
printf ("Do you like Win8?");
printf("And put up his hands.");
}
...
if(strcmp(name, "bill gates") == 0) {
BillgatesShowtime();
} else if(strcmp(name, "Steve jobs") == 0) {
StevejobsShowtime();
} else if(strcmp(name, "Zuckerberg") == 0) {
ZuckerbergShowtime();
} else {
...
噢,看上去確實好多了,我們可以讓老比爾給大家舉手示意了,不是嗎?
是的,你興奮了很久,甚至晚上睡覺都帶著醉人的笑容。
順便還做了個夢:你站在云端,覺得孤獨,便心血來潮的說:要有比爾,于是比爾出現了;你又說:要有喬布斯,于是喬布斯出現了;你接著說:要有馬化騰……
3 青銅器時代
恩,或許在這之前,還要經歷一個后石器時代,在那個過程中,我們使用typedef定義函數指針,并建立名字與函數地址關聯的數組(或者map),使代碼變得更優雅;這確實是一個很大的改善,但仍然還不足以代表設計模式上的進步,所以我們忽視他的存在。
3.1 痛苦的持續
哦,時光悠悠,石器時代過去了。
我們的先祖仍在期待神器,仍在尋找更鋒利的割肉工具……
代碼界,你跌坐在屏幕前,嘴里念念有詞:果然應驗了,我TM說“要有馬化騰”干毛啊?
是的,可惡的產品童鞋說了,要把馬化騰加入這個名人行列,同時還要抱一只企鵝。
在一萬只草泥馬奔騰過后,你從泥濘的草原上站起來,向著太陽的方向,前進前進前進進……
3.2 曙光
好在先祖們發現了青銅,相比之下,青銅比石頭和牙齒更靠譜,還能自己決定著割肉工具的外形(嚯嚯,越拉風越好哇)。
代碼界里,你也在進步:
...
class CPeople {
virtual void showtime() = 0;
};
...
class CXiaomage : public CPeople {
void showtime() {
printf("Fuck 360.");
printf("Do you like QQ pets?");
}
};
...
class CFamousPersons {
…
private:
map<string, CPeople*> peoples;
public:
CFamousPersons() {
peoples.insert(pair<string, CPeople*>("bill gates", new CBillgates()));
peoples.insert(pair<string, CPeople*>("steve jobs", new CStevejobs()));
peoples.insert(pair<string, CPeople*>("馬化騰", new CXiaomage()));
…
}
People* find(string name) {
map<string, CPeople*>::iterator iter = peoples.find(name);
if(iter != peoples.end())
return iter->second;
return NULL;
}
};
...
CFamousPersons famousPersons;
People* people = famousPersons.find(name);
if(people != NULL) {
people->showtime();
}
...
咦?代碼好像變多了,但我們為你自豪;因為這已經是一個完整的工廠模式的實現了。
是的,代碼可讀性增強了,邏輯結構也清晰了很多,對不對。關鍵是你再也不同擔心產品給你增加名人,不用擔心名人的各種性格和癖好。
于是你睡了個好覺,但你誠惶誠恐,不敢做夢。
4 鐵器時代
4.1 憤怒的燃燒
青銅的冶煉和鍛造技術已經達到了巔峰。吳鉤、魚腸、干將、莫邪……,喔噢,這已經是割肉神器了不?
仿佛從現在開始,擔心先祖的生活已經有點多余了。
是的,因為代碼界里,名人的數量已經達到了10000+(名人的各種性格和癖好,也讓你震驚到了無以復加)。
你已無數次的被這份名單中人從夢中驚醒,你的生活開始變得枯燥,你的世界漸漸黑白。仿佛你的生活就是為了這份名單而存在(有的名人改名了;有的名人換公司了;有的名人變性了,擦啊啊啊……)。
CFamousPersons已經被神獸草泥馬踐踏了無數次,而且變成了工程里面個頭最大的源代碼文件;每次你從peoples里找名人的時候,這份超長的名單幾乎亮瞎了你的鈦合金眼。
一次次的代碼更新,SVN上的代碼號直接蹦到了5位數;當你部署代碼時,看著運維冒著幽光的怨恨眼神,你叔忍了,但你嬸認為絕不能忍;于是你發糞圖墻……
4.2 福音
有一天,某位先祖發現自己的神器青銅大劍居然被一柄小匕首斬斷了,擦啊,這是什么玩意?超神器?
呵呵,豬腳模式開啟了,在代碼界,你也發現了“神器”。
你先把這個名單弄到了數據庫里(表名famous_persons):

然后,你用這把“神器”大刀闊斧把臃腫的CFamousPersons剁的稀爛。
再然后,你重造了一個輪子:
...
typedef void* (__stdcall *PExportClass)();
…
class CFamousPersons {
…
private:
CConnection conn;
...
const char* findPlugin(string name) {
char* pluginFile = "";
string sqlCmd = "SELECT * FROM famous_persons WHERE Name=\"";
sqlCmd += name;
sqlCmd += "\"";
CRecordset* rs = conn.executeQuery(sqlCmd);
if(!(rs->Eof() || rs->Bof())) {
pluginFile = new char[256];
strncpy(pluginFile, rs->fieldValue(2), 256);
}
rs->destroy();
return pluginFile;
}
public:
CPeople* find(string name) {
CPeople* people = NULL;
string pluginFile = findPlugin(name);
if(!pluginFile.empty()) {
HMODULE plugin = LoadLibrary(pluginFile);
if(plugin) {
PExportClass exportClass = (PExportClass)GetProcAddress(plugin, "exportClass");
if(exportClass)
people = (CPeople*)exportClass();
}
}
return people;
}
};
...
哇噢,酷!從此,你遠離了CFamousPersons;遠離了那糟糕的日子。生活貌似從這一刻又重新燃起了希望……
嗯,出乎意料的,你又做夢了,在夢里,你拋棄了數據庫,增加了好些個輔助模塊,代碼簡潔而優雅,充滿美感……
5 蒸汽機時代
5.1 貪婪的欲望
終于,當我們的先祖們還沉浸在冷兵器時代,突如其來的炮火,把國門轟得稀爛;戰火不斷,黃金白銀都頂不住火藥的貪婪……
代碼界里,你也終究沒來得及親自嘗試拋棄數據庫的喜悅,各種框架猝不及防的降臨。
5.2 神賜
好吧,時間線已經很接近現實,不過先祖們始終沒有得到神賜,一切都靠自力更生。
代碼界里不一樣,我們有了很多框架(神器)。比如說COM(恩,還有個學模學樣的跨平臺的XPCOM)。說起COM(應算是業界如雷灌招風耳的大神器了),聽上去好像不是很沾邊,但COM的CreateInstance其實現原理和應用機制,仍然是工廠模式。
當然,實現形式有了很大變化,就如他不采用名字,而是采用GUID映射;他不用數據庫,而是使用注冊表保存映射關系;他甚至還有自己的內存機制;有自己的數據類型等等。(廣告:預知詳情,請關注后續分享)。
6 另一個夢想
……