cocos2d-x設計模式發掘之一:單例模式

      做者: firedragonpzy  原地址:http://www.firedragonpzy.com.cn/index.php/archives/1781php

      本系列文章我將和你們一塊兒來發掘cocos2d-x中所使用到的設計模式,一樣的,這些模式在cocos2d-iphone中也能夠找到其身影。java

       聲明:這裏發掘模式只是個人我的愛好,經過這個過程,我但願能加深本身對於設計模式運用的理解。關於模式的學習,市面上已經有許多很是好的書籍了。好比《Head First設計模式》、GoF的設計模式,還有《研磨設計模式》等。若是讀者對於設計模式徹底不瞭解的話,建議先閱讀上面至少一本書籍,瞭解設計模式以後再閱讀本系列文章。這樣你們纔會有相互交流的共同語言。ios

       爲何要發掘設計模式呢?由於設計模式自己就是人們在一些面向對象的軟件系統裏面發掘出來的,在必定的場景之下能夠重用的解決方案。經過對模式的挖掘,我能夠藉此機會學習一下這些優秀的設計思想。由於我以爲,一個好的開源遊戲框架除了能給咱們開發者帶來開發效率的提高之外,還應該被咱們吸取其設計思想,這樣它的價值才能完整。程序員

       本系列文章將按照以下思路進行模式挖掘:編程

1.找出某個設計模式的應用場景(類、類結構、對象結構,必要時結合UML類圖)設計模式

2.分析爲何要使用此模式(即設計決策)緩存

3.使用此模式的優缺點是什麼(任何事務都有兩面性,設計模式也不例外)安全

4.此模式的定義及通常實現(這個在GoF的經典書籍裏面有,這裏借用一下)微信

5.在遊戲開發中如何運用此模式(本身對於模式運用場景的理解)框架

6.此模式常常與哪些模式配合使用(這個也基本是從GoF的書籍裏面借用了)

1.Cocos2D-x中的單例以下:

  • CCDirector

  • CCTextureCache

  • CCSpriteFrameCache

  • CCAnimationCache

  • CCUserDefault

  • CCNotificationCenter

  • CCShaderCache

  • CCScriptEngineManager

  • CCPoolManager

  • CCFileUtils

  • CCProfiler

  • SimleAudioEngie

  • CCConfiguration

  • CCApplication

  • CCDirectorCallerios平臺)

  • CCEGLView

爲何會存在這樣一些單例呢?

        首先是CCDirector單例,它負責管理初始化OpenGL渲染窗口以及遊戲場景的流程控制,它是cocos2dx遊戲開發中必不可少的類之一。爲何要把此類設計成單例對象呢?由於,一個遊戲只須要有一個遊戲窗口就夠了,因此,只須要初始化一次OpenGL渲染窗口。並且場景的流程控制功能,也只須要存在一個這樣的場景控制對象便可。爲了保證CCDirector類只存在一個實例對象,就必須使用單例模式。

       接下來是CCTextureCache單例。此類主要負責加載遊戲當中所須要的紋理圖片資源,這些資源加載好之後,就能夠一直保留在內存裏面,當下次再須要使用此紋理的時候,直接返回相應的紋理對象引用就能夠了,無需再重複加載。固然,這樣作可能會很浪費內存,因此cocos2dx採用了一種引用計數的方式來管理對象內存,當紋理剛被加載進來的時候,引用計數爲1。若是使用此紋理對象建立一個精靈,那麼此紋理對象引用會加1.若是精靈被釋放,則相應的引用計數減1.當紋理的引用計數變爲0的時候,紋理所佔用的內存天然就會被釋放掉。這也是爲何在收到內存警告的時候,會調用CCTextureCache       removeUnusedTextures方法。此方法會將全部引用計數爲1的紋理對象所有釋放掉。單從字面上看,Cache,即緩存的意思。它以犧牲必定的內存壓力爲代價,帶來的是遊戲性能的提高。這種cache技術,在遊戲開發中比比皆是。注:IO操做對遊戲性能影響很是大,要極力避免!!!

       相似的CCSpriteFrameCacheCCAnimationCacheCCShaderCache,它們也都是緩存類,分別負責緩存SpriteFrameAnimationShader。這樣作的緣由無非就是爲了性能,以空間換時間。

       接下來,看看CCUserDefault。此類主要是用來保存遊戲中的數據用的,它會建立一個xml文件,並把用戶自定義的數據以key-value的形式存儲到此xml文件中。此類爲何會變成單例類呢?緣由也很簡單,由於相似這種操做數據文件,或者配置文件的類,一般只須要在程序運行過程當中存在一個實例便可。

       接着是CCNotificationCenter,這是一個通知中心,它其實還運用了一個觀察者模式,這裏暫時不討論。該通知中心理論上也是隻須要一個就夠了。可是,cocos2d-x在實現此單例的時候,並無將此類的構造函數私有麼,我在猜測,是否是開發人員有意爲之呢?或者多個通知中心也有其存在的價值。這個你們能夠討論一下。

       CCScriptEngineManager,此類包含一個實現了CCScriptEngineProtocl接口的對象引用,它能夠幫助咱們方便地找到LuaEngine對象。這裏單例的做用純粹變成了LuaEngine的一個全局訪問點了。爲何不直接把LuaEngine做爲單例對象呢?是否在某些狀況之下,可能須要建立多個LuaEngine對象?若是考慮到cocos2d-x還能夠同時支持其它的腳本引擎,那也能夠相應的把另外的腳本引擎設計成單例類。固然,這樣作無疑會使引擎裏面的單例過多。考慮到單例模式近年來被廣大開發者所詬病,已將其列入反模式。這裏引用CCScriptEngineManager單例類,給其它引擎對象提供訪問的唯一全局點,這也不失爲一個辦法。不知個人推測是否正確?

       CCPoolManager,此類是用來管理AutoReleasePool對象棧的。由於cocos2d-x採用的是基於引用計數的方式來管理動態內存,因此採用引用計數的被託管對象都被放入一個當前的autoReleasePool裏面去了。當CCDirectormainLoop每次更新的時候,都會調用CCPoolManagerpop方法,把當前autoReleasePool裏面的全部autoRelease對象的託管狀態設置爲false,同時把該autoReleasePool清空,而清空的時候則會調用       autoReleasePool裏面全部對象的release方法來釋放內存。此類爲何要設計與單例呢?   由於多個地方須要引用此類,爲了方便引用,因此設計成單例。

       而後是CCFileUtils類。該類是一個工具類。工具類和配置文件類,它們絕大多數狀況也都是設計成單例的。由於它們沒有存在多個實例的必要。同時,它們也能夠實現爲一組類方法,這樣無需建立對象也可以使用。

       而後是CCProfiler類,該類負責cocos2d的性能其運行狀況分析,也是一個工具類。因此它設計成單例類的理由與CCFileUtils類差很少。

       CCConfiguration類也被設計成了單例對象,此類主要負責管理cocos2d-x的一些OpenGL變量信息。這些信息本能夠經過定義一些宏,或者經過一些全局變量來解決的。這裏設計成單例類也是更加面向對象的體現。由於這些配置信息根本不需存在多個對象。

      CCApplication類的設計初衷是得到平臺相關的一些信息,最重要的是運行遊戲的主循環(main loop)。一個遊戲只須要一個應用程序實例,因此設計與單例。

       CCEGLViewCocos2d-x對於EGLView的抽象,不一樣的平臺會有不一樣的實現,使之能夠適用不一樣的平臺。在ios平臺上面它是對EAGLView的一個簡單的封裝。該類表示的是對OpenGL渲染上下文窗口的一種抽象,這是一種虛擬資源,並且只有存在一份實例的可能,因此被設計成單例類。

       CCDirectorCaller類是ios平臺相關的類,就是對ios平臺CCDirector對象的一個封裝,使用的是CADisplayLink來運行遊戲主循環。該類和CCDirector類差很少,也能夠設計成單例。爲何會在CCApplication類裏面調用CCDirectorCaller類,是基於分離平臺相關代碼的考慮。CCApplication是的,CCDirectorCaller也是的。

    最後一個是SimpleAudioEngine類,它也被設計成了一個單例類。由於它提供給了開發人員最簡單的聲音操做接口,能夠方便地處理遊戲中的背景音樂和音效。此類同時還應用了外觀模式,把CocoDenshion子系統中的複雜功能給屏蔽起來了,簡化了客戶端程序員的調用。該類爲何要設計成單例,是由於處處都要訪問它。設計成單例會很方便,並且它   與其它對象沒有什麼聯繫,很差使用對象組合。

2.使用單例模式的優缺點   

    優勢:簡單易用,限制一個類只有一個實例,能夠下降建立多個對象可能會引發的內存問題的風險,包括內存泄漏、內存佔用問題。

    缺點:單例模式由於提供了一個全局的訪問點,你能夠在程序的任何地方輕而易取地訪問到,這自己就是一種高耦合的設計。一旦單例改變之後,其它模板都須要修改。另外,單例模式使得對象變成了全局的了。學過面對對象編程的人都知道,全局變量是很是邪惡的,要儘可能不要使用。並且單例模式會使得對象的內存在程序結束以前一直存在,在一些使用GC的語言裏面,這其實就是一種內存泄漏,由於它們永遠都不到釋放。固然,也能夠經過提供一些方法來釋放單例對象所佔用的內存,好比前面提到的XXXCache對象,都有相應的Purge方法。最後,cocos2dx裏面實現的單例,99%都不是線程安全的。
   
在討論優缺點的時候,讀者想必也看出來了,缺點比優勢多多了。這是給你們提個醒,之後使用單例模式的時候要謹慎,不要濫用。由於此模式最容易被濫用。只有真正符合單例模式應用場景的時候,才能考慮。不要爲了訪問方便,就把任何類都弄成單例,這樣,到最後,你會發現你的程序裏面就只剩下一堆單例和工廠了。
   
此外,單例模式正在消減,好比CCActionManagerCCTouchDispatchercocos2d1.0以前也是單例,如今變成了CCDirector類的屬性了。並且Riqcocos2d-iphone的做者)也有在郵件中提到,之後CCDirector對象也會變成非單例,而且容許一個遊戲中建立多個遊戲窗口。

3.單例模式的定義:保證一個類僅有一個實例,並提供一個訪問它的全局的訪問點。

    它的通常實現以下所示:

  publicclass Singleton
  {
     public:
     //全局訪問點
     staticSingleton*
  SharedSingleton()
     {
         if(NULL == m_spSingleton)
         {
             m_spSingleton = newSingleton();
         }
         returnm_spSingleton;
     }
    private:
    staticSingleton*
  m_spSingleton;
    Singleton();
    Singleton(constSingleton&
  other);
    Singleton&
  operator=(constSingleton&
  other);
  };
  Singleton* Singleton::m_spSingleton = NULL;

     注意,這裏只是最基本的實現,它沒有考慮到線程安全,也沒有考慮內存釋放。可是,這個實現有兩個最基本的要素。
一:定義一個靜態變量,並把構造函數等設置爲私有的。二:提供一個全局的訪問點給外部訪問。

       4.遊戲開發中如何運用此模式呢?衆所周知,遊戲開發中離不開遊戲數據保存和加載。這些數據包括關卡數據、遊戲進行中的狀態數據等。這樣一些信息不少遊戲模塊中都須要訪問,因此能夠爲之設置一個單例對象。我武斷地認爲,客戶端遊戲開發中,至少須要一個單例對象。由於一個全局的訪問點能夠方便不少對象之間的交互。根據以前的討論,也能夠把一些時覺須要用到的類引用保存在此單例對象中,不過只須要保存弱引用便可。使用單例,最嚴重的就是怕內存泄漏,因此,你們儘可能不要把單例類設計地太複雜,也不要讓它包含過多的動態內存管理工做。

       5.單例模式通常與工廠模式配合使用,由於通常會將工廠類設計成單例對象。好比前面提到的各類cache類,它們也能夠看做是某種意義上的工廠對象。因爲工廠就是負責生產對象的,而cache類均可以根據用戶的須要生產出相應的對象。

     最後,看看cocos2d-x創始人王哲對於什麼是單例的見解:
這麼說吧, 我設計成單例基本就一種抽象狀況:獨佔性資源。好比某個硬件IO(如CCTouchDispatcher, CCAccelerometer),好比公用的緩存區(CCTextureCache, CCUserDefault)。後來有人抱怨單例類太多,想銷燬整個cocos2d instance再重建很麻煩,因此小明和riq就把大量單例類放到CCDirector裏面管理。
摘自:http://www.zilongshanren.com/cocos2d-x-design-pattern-singleton1/comment-page-1/#comment-20


 歡迎關注關東昇新浪微博@tony_ 關東昇。

關注智捷課堂微信公共平臺,瞭解最新技術文章、圖書、教程信息

更多精品iOS、Cocos、移動設計課程請關注智捷課堂官方網站:http://www.zhijieketang.com

智捷課堂論壇網站:http://51work6.com/forum.php

相關文章
相關標籤/搜索