這一篇將來學習“外觀模式”,在cocos2d-x引擎中,一個採用此設計模式的經典類就是SimpleAudioEngine,此模式的核心思想是,爲子系統中的一組接口提供一個一致的界面,它定義了一個高層接口,這個接口使得上層使用子系統更加容易,同時,它實現了子系統與上層之間的鬆耦合關係,而子系統內部的功能組件往往是緊密耦合的,這樣當子系統功能組件發生變化的時候,只需要修改外觀類的實現就可以了,避免了程序代碼的“散彈式”修改。
1.應用場景
Cocos2d-x裏面有一個非常明顯的地方使用了外觀模式,它就是SimpleAudioEngine。因爲它爲CocosDenshion這個子系統的一組接口提供了一個一致的界面,同時定義了一個高層接口,方便客戶使用該子系統。
對於大多數用戶來講,遊戲中操作聲音,無非就是播放背景音樂和音效。CocosDenshion這個子系統封裝了OpenAL,屏蔽了OpenAL操作聲音的低級API。它提供了CDSoundEngine、CDAudioManager兩個類來操作和管理聲音。具體這兩個類是如何工作的這裏就不再討論了,感興趣的讀者可以自行去研究相關代碼。雖然CocosDenshion子系統已經封裝了低級的操作聲音的API,但是對於用戶來講,還是得了解該系統內部的類是如何一起協作來完成聲音處理任務的。這樣會加大用戶使用此子系統的難度,同時,也使得客戶程序與該子系統緊密耦合了。假如哪一天該子系統內部實現功能的組合有所變化,這勢必會影響到客戶程序。衆所周知,操作遊戲音樂的代碼是分散在遊戲代碼各處的,那樣會造成“散彈式”修改。這是個嚴重的代碼壞味道,需要引起警覺,果斷重構之!
而外觀模式就可以完美地解決此問題,SimpleAudioEngine就是最好的例子。如果使用過SimpleAudioEngine的人會發現,它實在是太簡單了。但是,SimpleAudioEngine並不是萬能的,比如,它就無法實現循環播放音效的功能。但是,沒有關係,你可以使用CDSoundEngine來實現這個功能。
請注意,SimpleAudioEngine並沒有增加新的功能,而只是把子系統現有的類進行組合來完成一些常用的任務,簡化客戶程序的使用。子系統對於外觀類是不知情的,即子系統不會包含外觀類的指針。
2.使用外觀模式的優缺點
優點:
1)它對客戶屏蔽子系統組件,因而減少了客戶處理的對象的數目,並使得子系統使用起來更加方便。
2)它實現了子系統與客戶之間的鬆耦合關係,而子系統內部的功能組件往往是緊密耦合的,這樣當子系統功能組件發生變化的時候,只需要修改外觀類的實現就可以了,避免了程序代碼的“散彈式”修改。
3)同時,外觀類並不限制客戶直接使用子系統的功能組件,如果客戶想使用子系統的更加高級的功能,可以越過外觀類直接訪問子系統的類。
缺點:
1)過多的或者不太合理的Faade也容易讓人迷惑。到底是調用Faade好呢,還是直接調用子系統的模塊好呢。
3.外觀模式的定義及一般實現
UML圖:
定義: 爲子系統中的一組接口提供一個一致的界面,它定義了一個高層接口,這個接口使得子系統更加容易使用。它很好地體現了“最少知識原則”。
它的本質是:封裝交互、簡化調用。
實現(摘至維基百科):
考慮下面一個例子:
設計你(You)如何與一臺計算機(facade)進行交互,而計算機是一個非常複雜的系統,它內部包含CPU、HardDrive等。
/* Complex parts */ class CPU { public void freeze() { ... } public void jump(long position) { ... } public void execute() { ... } } class Memory { public void load(long position, byte[] data) { ... } } class HardDrive { public byte[] read(long lba, int size) { ... } } /* Facade */ class Computer { private CPU cpu; private Memory memory; private HardDrive hardDrive; public Computer() { this.cpu = new CPU(); this.memory = new Memory(); this.hardDrive = new HardDrive(); } public void startComputer() { cpu.freeze(); memory.load(BOOT_ADDRESS, hardDrive.read(BOOT_SECTOR, SECTOR_SIZE)); cpu.jump(BOOT_ADDRESS); cpu.execute(); } } /* Client */ class You { public static void main(String[] args) { Computer facade = new Computer(); facade.startComputer(); } }
4.遊戲開發中如何運用此模式
遊戲開發過程中,暫時還沒發現此模式的明顯用法。不過,模式不是說學習了一定要馬上就用到,那樣會導致過度設計。如果讀者開發遊戲過程中,積累出一套比較成熟的框架,而這個框架又可以劃分多個子系統,比如碰撞子系統、網絡子系統、數據持久化子系統等。當外部使用此子系統時,操作的類過多,理解起來特別複雜時,這時候就可以考慮引入一個Faade類,來簡化客戶程序與子系統之間的調用關係。
5.外觀模式與其它模式的關係
通常來講只需要一個外觀類,所以可以採用單例模式。