剛接觸設(shè)計模式的第一課,工廠模式與抽象工廠,確實感覺到了設(shè)計模式的抽象與強大作用力,學(xué)習(xí)過程中自己動手設(shè)計了一個小游戲的角色。
假定一個游戲中有很多怪物角色,如蜘蛛,馬,猴子,等,此游戲有多種游戲級別,先假定為3級。
設(shè)計游戲時必須考慮到:
1,游戲的角色可擴展性
2,游戲易于維護(如,游戲中怪物角色易于管理)
暫時不考慮其他的問題,先說說角色的設(shè)計問題。
游戲中有很多怪物角色,也分為三級,即怪物也有三個級別,那么,怎么設(shè)計角色的繼承體系呢?
至少有以下兩種策略:
a),游戲維護一個怪物超類,所有怪物直接繼承,將這些子類再作為超類,供“三個級別”怪物類繼承,類結(jié)構(gòu)大致為:
CMonster
CHouse :public CMonster,......
CHighLevelHouse :public CHouse,CLowLevelHouse :public CHouse.....
b),游戲維護一個怪物超類,讓三個級別繼承此超類,再分別讓每個怪物繼承之,類結(jié)構(gòu)大致為:
CMonster
CHighLevelMonster : public CMonster,CLowLevelMonster : public CMonster.......
CHighLevelHouse : public CHighLevelMonster,CLowLevelHouse: public CLowLevelMonster....
暫且不論這兩種繼承方案怎樣,下面就這兩種方案分別使用工廠模式和抽象工廠得到怪物對象。
一,使用工廠模式
使用工廠模式的目的在于代碼的客戶不需要親自實例化一個對象,客戶只需要操心:“我要得到什么”,而不用操心:“我要使用哪一個具體的類去獲得”;考慮在可預(yù)見的將來客戶需要的對象改變了,那么,他是否不得不去更改那一段new Chouse()的代碼?但如果使用工廠方法,替代這種“硬編碼”,或言之“過程性編碼”,則他不需要更改任何代碼,而只需要更改工廠里生產(chǎn)此對象的方法,將生產(chǎn)的對象替換一下就能做到。這是很具有優(yōu)越性的,假定程序中出現(xiàn)了一萬次new Chouse(),后來我們不再需要Chouse這個對象,而是需要重寫或者有較Chouse有大更改的ChouseChanged 類實例,這就意味著一天悲慘的查找->替換工作的開始!但如果使用了工廠方法,以一種封閉的方法產(chǎn)生客戶需要的對象,就只需要在工廠內(nèi)部修改生成的方法,替換一次即可!
以上是使用的目的!但如何使用呢,下面就來深入的了解工廠模式。工廠從某種角度上就是一種透明的機器,這種機器有很多型號,有的能生產(chǎn)猴子對象,有的能生成蜘蛛對象,等等。那么,為什么不是“一部透明的機器”呢,因為它要生產(chǎn)很多種對象,“一部機器”是不能生產(chǎn)末知種類,末知數(shù)量的對象的。那,為什么不將某參數(shù)傳入此機器,讓它能根據(jù)參數(shù)生產(chǎn)特定的對象呢?答案在于,可擴展性。如前所言,工廠并不知道它要生產(chǎn)怎樣的對象,生產(chǎn)多少種類型的對象,如果單純地以參數(shù)去界定,若以后再增加了一/多種對象,則還需要修改工廠的生產(chǎn)方法,可能是增加switch里面的case語句,這樣就涉及到維護問題了,一旦忘記,則導(dǎo)致新加的對象創(chuàng)建不成功!記住,工廠本應(yīng)是一個抽象的概念,不是一個具體的概念。所以生產(chǎn)多種對象,必須有多種具體的工廠!如,生產(chǎn)猴子的猴子工廠,生產(chǎn)蜘蛛的蜘蛛工廠,等。這就是工廠模式。
二,使用抽象工廠模式
再來說說抽象工廠模式,其實本人覺得抽象工廠模式也就是工廠模式,只是它是前一種模式復(fù)雜一些的模式,但本質(zhì)還是一樣的,如果你會用工廠模式,那么你一定會用抽象工廠模式!
已經(jīng)知道了為什么要使用工廠模式,現(xiàn)在讓我們開始分析在那個游戲中,怎樣通過工廠模式去獲得怪物。
現(xiàn)在的情況稍微復(fù)雜了一些,增加了游戲的難易級別,則不能簡單的直接使用工廠模式,因為無法滿足三個級別的限制,那么,抽象工廠便出現(xiàn)了,其實質(zhì)是將工廠再次向上抽象,產(chǎn)生繼承得到多個抽象的工廠。
考慮將工廠歸類,則至少有以下兩種策略:
a),定義三個“級別工廠”:高級怪物工廠,中級怪物工廠,低級怪物工廠,讓每個工廠去生產(chǎn)所有類型的怪物
b),定義多個“怪物工廠”:猴子工廠,蜘蛛工廠,馬工廠,讓每個工廠去生產(chǎn)三種怪物(高級,中級,低級)
首先,我們先決策哪一種策略更優(yōu),把“更優(yōu)”換一種說法,即是文章最開始說到的兩個條件:可擴展性,可維護性。先考察可擴展性:假設(shè)游戲以后增加了幾十種,幾百種,新的怪物,則b中的怪物工廠則飆增到相同的數(shù)目,相比之下 a 中的工廠數(shù)目則不會增加。但 a 也會付出慘重的代價:每個工廠里面,增加幾十個,幾百個生產(chǎn)對象的方法。有一個經(jīng)驗,“集中地增加代碼,而不是分散地增加或者修改既有代碼”往往表現(xiàn)出更優(yōu)的可擴展性。顯然,定義怪物工廠可獲得更好的可擴展性。因為在b中,新增加一種怪物,只需要增加一個工廠,同時在里面寫入三個方法分別產(chǎn)生三種級別的怪物即可,相比a中的在三個工廠中都增加一個方法(不能遺忘),顯然更優(yōu)一些!但b也不是最優(yōu)解,因為如果游戲級別一旦增加,則需要在每個怪物工廠中去增加相應(yīng)的代碼,而a中則只需要再新增加一個工廠,并將其它工廠里的代碼直接拷到下面即可工作。但我們之前有約定:怪物的易變性大于級別的易變性,畢竟,一個游戲的游戲級別是不會常變的,這也符合假定!如果要徹底解決這個問題,我們得時刻記住一句話:“永遠要對變化的東西抽象(封裝)”。于是,我們自然會想到,使用繼承去獲得游戲級別變化下的可擴展性,我們可以在b的基礎(chǔ)上再使用一層抽象,讓怪物工廠里的生產(chǎn)怪物的方法是抽象的,(C++里使用vitual修飾),讓每個怪物工廠被三個類(目前是三個級別)去繼承,可能是這樣的類結(jié)構(gòu):
CFactory
CHouseFactory : public CFactory,CMonkeyFactory : public CFactrory,.......
CHighLevelFactory : public CHosueFactory,CLowLevelFactory : public CHouseFactory....
這樣會獲得完全的擴展性,但與之相應(yīng)的代價是子類膨脹問題,子類巨多!每個怪物工廠類有三個子類!
這里可以做權(quán)衡,減少可擴展性,增加新函數(shù)代替子類:可能的結(jié)構(gòu)如下:
CFactory
{
virtual CHighLevelMonster* getHighLevelMonster();
virtual CMiddleLevelMonster* getMiddleLevelMonster();
virtual CLowLevelMonster* getLowLevelMonster();
//可能只有這三種級別
}
CHouseFactory : public CFactory
{
CHighLevelMonster* getHighLevelMonster();
CMiddleLevelMonster* getMiddleLevelMonster();
CLowLevelMonster* getLowLevelMonster();
}
說明:
1,在超類工廠CFactory中任何獲得對象的方法都是virtual的
2,抽象工廠中的獲得對象的方法的返回值類型必定不同!(第一個是CHighLevelMonster*,第二個是CMiddleLevelMonster*,第三個是CLowLevelMonster*)。謹記:如果它們的返回值一樣,則退化為了工廠模式,而不是抽象工廠模式!之所以叫抽象工廠,是因為抽象工廠一旦實例化,則可以實例化為多種類型的工廠,每一種工廠用來生產(chǎn)相關(guān),但不同類型的對象,各種實例化出來的工廠之間的差別在于,它們生產(chǎn)這些對象的方式或者結(jié)果不同!游戲需要三種級別的怪物,則每個工廠按自己的方式去生成它們,(“不同的方式”體現(xiàn)在,每個工廠生產(chǎn)出的怪物(即使級別相同,但)不同)
三,反過來再談繼承方案
在文章的最開始,我們提出了兩種繼承策略,
a),游戲維護一個怪物超類,所有怪物直接繼承,將這些子類再作為超類,供“三個級別”怪物類繼承,類結(jié)構(gòu)大致為:
CMonster
CHouse :public CMonster,......
CHighLevelHouse :public CHouse,CLowLevelHouse :public CHouse.....
b),游戲維護一個怪物超類,讓三個級別繼承此超類,再分別讓每個怪物繼承之,類結(jié)構(gòu)大致為:
CMonster
CHighLevelMonster : public CMonster,CLowLevelMonster : public CMonster.......
CHighLevelHouse : public CHighLevelMonster,CLowLevelHouse: public CLowLevelMonster....
我們前面使用的抽象工廠模式正是基于的第二種繼承方案,所以被抽象的工廠是“怪物工廠”,而不是“級別工廠”,然后在各個工廠里生產(chǎn)出的是不同級別的怪物。若我們使用第一種繼承方案,由正好對應(yīng)了將工廠歸類中的 a) ,定義三個“級別工廠”:高級怪物工廠,中級怪物工廠,低級怪物工廠,讓每個工廠去生產(chǎn)所有類型的怪物此時,形成的是“級別工廠”,在每個級別工廠里,生產(chǎn)出的是級別相同但屬性不同的怪物,可能的代碼是這樣的:
CFactory
{
virtual CMonkey* getMonkey();
virtual CHouse* getHouse();
virtual CSpiter* getSpiter();
.............//省略掉其它怪物
}
CHighLevelFactory : public CFactory
{
CMonkey* getMonkey();
CHouse* getHouse();
CSpiter* getSpiter();
....................
}
根據(jù)之前的討論,b)方法的抽象工廠模式較好,所以繼承方案應(yīng)該是后一種要好一些
四,結(jié)語
經(jīng)過之前的分析,對工廠模式和抽象工廠模式有了一定的認識,可能還存在不足之處,望讀者指出,一同探討!