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      另一个梦想

……