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(應算是業界如雷灌招風耳的大神器了),聽上去好像不是很沾邊,但COMCreateInstance其實現原理和應用機制,仍然是工廠模式。

當然,實現形式有了很大變化,就如他不采用名字,而是采用GUID映射;他不用數據庫,而是使用注冊表保存映射關系;他甚至還有自己的內存機制;有自己的數據類型等等。(廣告:預知詳情,請關注后續分享)。

6      另一個夢想

……