開宗明義 話說設(shè)計(jì)模式以來,眾人皆奉為經(jīng)典,其實(shí)說穿了不過是一個(gè)矛盾的辯證關(guān)系而已,即如 何將變化與固定相統(tǒng)一的問題。通常的代碼是固定的,很難變化; 而需求與環(huán)境是變化的,故而兩者是矛盾的。但我們卻要在程序中將兩者的矛盾處理好。 然而,兩者又是統(tǒng)一的,因?yàn)樽兓褪菫榱?#8220;不變”,即最大限度地重用已有的“不變” 代碼;另一方面,將“不變”認(rèn)識(shí)清楚,就知道哪些該“變化”,哪些該讓它“變化”。 只有在固定的基礎(chǔ)上,將該變化的讓它變化,程序才能既牢固,又有彈性,從而能夠滿足 各方面的要求。后面的分析過程,希望大家能夠牢記這個(gè)要點(diǎn),從這個(gè)角度來看設(shè)計(jì)模式 ,而不是為學(xué)模式而學(xué)模式,那樣我認(rèn)為永遠(yuǎn)都沒有自己的領(lǐng)悟。而且設(shè)計(jì)模式也不是固 定的,隨著時(shí)間的推移會(huì)出現(xiàn)其它的新模式,但我想仍然是符合”變化與固定“這個(gè)原則 的。 好了,羅嗦了這么多,我們首先從常見的《創(chuàng)建型模式》說起,我首先不給出干巴巴的定 義,而是給出活生生的例子,從實(shí)踐出總結(jié)出規(guī)律,再給出定義就是水到渠成的事情了。
迷宮是許多游戲的組成元素,玩家在迷宮時(shí)探索,從一個(gè)入口進(jìn)入迷宮,在迷宮中穿行, 與遇到的敵人激戰(zhàn),撿取醫(yī)藥包、武器等物品,最后找到出口,其間經(jīng)常從一個(gè)房間進(jìn)入 另一個(gè)房間,碰了壁就走過其它的路,如此反復(fù)。姑且不考慮這么復(fù)雜,我們現(xiàn)在僅關(guān)心
迷宮的創(chuàng)建。遵循著“將問題簡(jiǎn)化到不能簡(jiǎn)化”的原則,我們假設(shè)此迷宮中僅有兩個(gè)房間 和一扇門,從其中一個(gè)房間推開門就可以進(jìn)入另一個(gè)房間。整個(gè)迷宮的坐標(biāo)是二維的,即 使用“東南西北”作為方位。 我們首先大致寫出迷宮中房間、門、墻的框架,再接著我們的討論(我們使用MapSite作為 所有組件的協(xié)議接口): interface MapSite { public void enter(); }
class Room implements MapSite { public Room(int roomNo) { this.roomNo = roomNo; }
public void enter() { }
public MapSite getSide(int n) { return _sides[n]; }
public void setSide(int n,MapSite ms) {
_sides[n] = ms; } }
class Wall implements MapSite { public Wall() { }
public void enter() { } }
class Door implements MapSite { public Door(Room r1,Room r2) { _room1 = r1; _room2 = r2; }
public void ener() { } }
//迷宮類 class Maze { public Maze() { }
public void addRoom(Room r) { }
public Room roomNo(int) { } }
有了磚瓦,就可以蓋房子了。我們利用這些原料來搭建一個(gè)迷宮。我們能想到的最自然、 最簡(jiǎn)單的方法,就是在迷宮游戲類的一個(gè)方法中寫下創(chuàng)建代碼: class MazeGame { public Maze createMaze() { Maze aMaze = new Maze(); Room r1 = new Room(1); Room r2 = new Room(2); Door theDoor = new Door(r1,r2);
aMaze.addRoom(r1);
aMaze.addRoom(r2);
r1.setSide(North,new Wall()); r1.setSide(East,theDoor); r1.setSide(South,new Wall()); r1.setSide(West,new Wall());
r2.setSide(North,new Wall()); r2.setSide(East,new Wall()); r2.setSide(South,new Wall()); r2.setSide(West,theDoor);
return aMaze; } } 在createMaze的代碼中,我們寫下了迷宮構(gòu)成部件的生成代碼和迷宮本身的布局代碼。那 么,我們從“變與不變的角度”可以知道,整個(gè)代碼部分都是可以變化的. 大家好好想一想,一個(gè)游戲可能會(huì)升級(jí),今天的一個(gè)房間里可能是一個(gè)醫(yī)藥包,明天可能 你得要鑰匙才能打開這個(gè)門取得這個(gè)醫(yī)藥包,后來還有可能是其它什么東東。 這么多哇?!別急,我們還知道,今天,這個(gè)迷宮的地圖是這樣,明天可能是那樣,后天 。。。打住,讀者可能頭都大了,你改來改去我的代碼不是成馬蜂窩了嗎? 我的代碼是死的,怎么可能一會(huì)兒是這個(gè),一會(huì)兒是那個(gè),不是要我命嗎?嘿嘿,別急,
我們要承認(rèn)這樣一個(gè)事實(shí)。[需求是不斷改變的],正如馬原所講[運(yùn)動(dòng)是絕對(duì)的,靜止是相 對(duì)的],只有正視這個(gè)問題,我們才能開發(fā)出合乎要求的軟件來。我決定對(duì)這個(gè)問題按兩個(gè) 方面來討論,首先解決迷宮中的門變化的問題,后面的文章再討論迷宮的布局變化的問題 ,必竟,飯要一口一口吃嗎!
請(qǐng)大家思考一下,上面的代碼要怎樣才能滿足變化的需要呢?首先,我們承認(rèn),代碼只有 變化,才能反映要求的變化,只是如何變化才巧妙而已。在程序代碼中,虛方法是變化的 不二法門。為什么這么說呢? 因?yàn)樘摲椒梢宰屇阍?#8220;不變”的基礎(chǔ)上達(dá)到“變化”的目的。不是嗎?所有的OO教材上 都把這一點(diǎn)作為OO的頭一優(yōu)點(diǎn)來大書特書,我們也來這樣操作一番??墒?,我要變化的創(chuàng) 建房子、墻、門,這些不可能改變呀,因?yàn)槌跏蓟恼Z法就限制了你必須使用構(gòu)造方法名 ,而構(gòu)造方法名與類名是相同的,你要改變成另一個(gè)類,就必須在迷宮創(chuàng)建代碼中將名稱 改變成新類的名稱。 OK,我們找到了問題的所在,我們要改變創(chuàng)建的類,而上面的代碼不讓我們改變,這是語 言限制,我們只要找一種既能創(chuàng)建自己的對(duì)象又能無需改變創(chuàng)建代碼的方法。沒關(guān)系,我 在這個(gè)迷宮代碼中非要使用構(gòu)造器來創(chuàng)建一個(gè)類的對(duì)象嗎? 不用,雖然本質(zhì)上我們最終還是要使用創(chuàng)建語法,但在上面的代碼中,我們完全不用這么 做?我們代之以虛方法? Room createRoom(int roomNo) { return new Room(roomNo); } 代入上面的代碼后,變?yōu)?br>
Room r1 = createRoom(1); Room r2 = createRoom(2); 如果再有新的房子出來,只要提供另一個(gè)createRoom的實(shí)現(xiàn),上面的代碼只要布局不變, 是不用改變的,不是嗎?OK! 只是,在OO中,所有的方法都應(yīng)該是成員方法或類方法,上面的方法是哪個(gè)類的呢?顯然 ,我們可以將createRoom作為MazeGame的成員方法。于是,我們又有了createWall和crea teDoor方法,要改變創(chuàng)建的東西,我們只要繼承MazeGame, 重寫createXXX方法,不就達(dá)到了“不變”的目的嗎?下面是新的MazeGame的代碼: class MazeGame { public MazeGame() { }
public Room createRoom(int roomNo) { return new Room(roomNo); }
public Wall createWall() { return new Wall(); }
public Door createDoor(Room r1,Room r2) { return new Door(r1,r2);
}
public Maze createMaze() { //ommit here } }
到此為止,大家學(xué)會(huì)了一種簡(jiǎn)單實(shí)用的設(shè)計(jì)模式,即Factory Method,希望大家再接再厲 ,跟我一起來了解一下Factory Method的理論,畢竟實(shí)踐清楚了,就要上升到理論高度, 同意嗎? 冷靜地分析一下,問題的關(guān)鍵在MazeGame類,此類預(yù)留了若干個(gè)接口,讓子類通過重寫這 些接口,來達(dá)到實(shí)例化“具體的類”,這些“具體的類”用來構(gòu)成Maze類。 這個(gè)框架能夠改變類的實(shí)體,但無法改變類之間的組合關(guān)系(這應(yīng)是本方法的缺點(diǎn))。再 來看,我們是將具體的創(chuàng)建Room、Wall、Door的操作抽象成對(duì)應(yīng)的方法,使得創(chuàng)建與其他 代碼隔離,達(dá)到重用這些其他代碼的目的。這也是“創(chuàng)建型模式”的名稱由來。
MazeGame有許多的接口,可以完成創(chuàng)建組成對(duì)象的工作,我們稱之為“工廠”,工廠中創(chuàng) 建對(duì)象的方法,我們稱之為“工廠方法”。 通過在具體的工廠中,重寫工廠方法,或者說生成具體的工廠方法,生產(chǎn)出具體的產(chǎn)品, 達(dá)到所需的效果,這應(yīng)是“工廠方法”的幾個(gè)要素。
|
|