單件的好處在于它可以在任何地方被任何類使用??梢园阉斫鉃橐粋€(gè)全局類(請(qǐng)參照“全局變量”的概念來(lái)加深理解)。如果在很多不同的地方需要使用同一組數(shù)據(jù)和方法,那么這時(shí)候單件就非常有用。音頻就是一個(gè)很好的示例,因?yàn)槿魏晤?無(wú)論是玩家類、敵方類、菜單類或過(guò)場(chǎng)動(dòng)畫(huà)類)都可能需要播放音效或切換背景音樂(lè),所以選擇用單件來(lái)播放音頻是明智之舉。與此類似,你可能希望將全局的游戲狀態(tài)信息(可能是玩家持有的兵器數(shù)目,也可能是軍隊(duì)的一個(gè)排有多少人,等等)存儲(chǔ)在一個(gè)單件中,這樣就可以在游戲的不同關(guān)卡中使用同一份數(shù)據(jù)了。
如代碼清單3-1所示,單件的實(shí)現(xiàn)相當(dāng)簡(jiǎn)潔。這個(gè)示例用最少的代碼實(shí)現(xiàn)了一個(gè)單件類:MyManager。其中,靜態(tài)方法sharedManager提供了對(duì)MyManager的單一實(shí)例的訪問(wèn):如果實(shí)例不存在,就為MyManager實(shí)例分配內(nèi)存并完成初始化;如果存在,就返回實(shí)例。
代碼清單3-1 用單件實(shí)現(xiàn)示例類MyManager
static MyManager *sharedManager = nil;
+(MyManager*) sharedManager
{
if (sharedManager == nil)
{
sharedManager = [[MyManager alloc] init];
}
return sharedManager;
}
不過(guò),單件也有它的缺點(diǎn)。正因?yàn)樗鼈円子趯?shí)現(xiàn)、方便使用,而且可以在其他任何類中使用,所以人們常會(huì)在一些不應(yīng)該用單件來(lái)實(shí)現(xiàn)的場(chǎng)合使用它們。
例如,你可能想:我只有一個(gè)玩家對(duì)象,為什么不把這個(gè)玩家類設(shè)為一個(gè)單件呢?這種想法似乎很完美。但你會(huì)突然發(fā)現(xiàn),只要玩家進(jìn)入下一個(gè)關(guān)卡,他就不僅帶著上一輪的分?jǐn)?shù),而且還保持著上一輪最后的動(dòng)作、血量、撿到的所有道具。甚至,他剛進(jìn)入下一關(guān)就已經(jīng)在“狂暴”模式(Berserk Mode)下了,而這恰恰是因?yàn)樗x開(kāi)上一關(guān)的時(shí)候就處于這個(gè)模式。
為了解決這個(gè)問(wèn)題,你會(huì)去添加幾個(gè)方法,在關(guān)卡發(fā)生變化時(shí)對(duì)一些變量進(jìn)行重置。到目前為止,一切都進(jìn)行得很順利,但是當(dāng)你為游戲設(shè)計(jì)更多特性時(shí),你會(huì)發(fā)現(xiàn)轉(zhuǎn)換關(guān)卡時(shí)需要添加好多變量,而且還要對(duì)很多已有變量進(jìn)行維護(hù)。更糟糕的是,假如有一天朋友建議你為iPad版本添加雙人模式,這時(shí)候你想起玩家類是個(gè)單件!任何時(shí)候都只能有一個(gè)玩家實(shí)例!于是你將面臨一個(gè)非常令人頭疼的問(wèn)題:要么重寫(xiě)大量代碼進(jìn)行徹底重構(gòu),要么放棄雙人模式。
越是依賴于單件,就會(huì)碰到越多這樣的麻煩事。所以,在你創(chuàng)建一個(gè)單件類之前,務(wù)必要考慮清楚,對(duì)于這個(gè)類以及它的數(shù)據(jù),是否真的只需要一個(gè)實(shí)例?這個(gè)設(shè)計(jì)以后是否會(huì)發(fā)生變化?