cocos2d-x設計模式發掘之二:二段構建模式
乍一看標題,大家可能會覺得很奇怪,神馬是“二段構建模式”呢?
所謂二段構建,就是指建立物件時不是直接通過構建函式來分配記憶體並完成初始化操作。取而代之的是,建構函式只負責分配記憶體,而初始化的工作則由一些名為initXXX的成員方法來完成。然後再定義一些靜態類方法把這兩個階段組合起來,完成最終物件的構建。因為在《Cocoa設計模式》一書中,把此慣用法稱之為“Two Stage Creation”,即“二段構建”。因為此模式在cocos2d裡面被廣泛使用,所以把該模式也引入過來了。
1.應用場景:
二段構建在cocos2d-x裡面隨處可見,自從2.0版本以後,所有的二段構建方法的簽名都改成create了。這樣做的好處是一方面統一介面,方便記憶,另一方面是以前的類似Cocoa的命名規範不適用c++,容易引起歧義。下面以CCSprite為類,來具體闡述二段構建的過程,請看下列程式碼:
//此方法現在已經不推薦使用了,將來可能會刪除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
CCSprite*
CCSprite::spriteWithFile( const
char
*pszFileName)
{
return
CCSprite::create(pszFileName);
}
CCSprite*
CCSprite::create( const
char
*pszFileName)
{
CCSprite
*pobSprite = new
CCSprite(); //1.第一階段,分配記憶體
if
(pobSprite && pobSprite->initWithFile(pszFileName)) //2.第二階段,初始化
{
pobSprite->autorelease();
//!!!額外做了記憶體管理的工作。
return
pobSprite;
}
CC_SAFE_DELETE(pobSprite);
return
NULL;
}
|
如上面程式碼中的註釋所示,建立一個sprite明顯被分為兩個步驟:1.使用new來建立記憶體;2.使用initXXX方法來完成初始化。
因為CCSprite的建構函式也有初始化的功能,所以,我們再來看看CCSprite的構建函式實現:
1 |
CCSprite::CCSprite( void ):
m_pobTexture(NULL), m_bShouldBeHidden( false ){}
|
很明顯,這個構建函式所做的初始化工作非常有限,僅僅是在初始化列表裡面初始化了m_pobTexture和m_bShouldBeHidden兩個變數。實際的初始化工作大部分都放在initXXX系列方法中,大家可以動手去檢視原始碼。
2.分析為什麼要使用此模式?
這種二段構建對於C++程式設計師來說,其實有點彆扭。因為c++的建構函式在設計之初就是用來分配記憶體+初始化物件的。如果再搞個二段構建,實則是多此一舉。但是,在objective-c裡面是沒有建構函式這一說的,所以,在Cocoa的程式設計世界裡,二段構建被廣泛採用。而cocos2d-x當初是從cocos2d-iphone移植過來了,為了保持最大限度的程式碼一致性,所以保留了這種二段構建方式。這樣可以方便移植cocos2d-iphone的遊戲,同時也方便cocos2d-iphone的程式設計師快速上手cocos2d-x。
不過在後來,由於c++天生不具備oc那種可以指定每一個引數的名稱的能力,所以,cocos2d-x的設計者決定使用c++的函式過載來解決這個問題。這也是後來為什麼2.0版本以後,都使用create函式的過載版本了。
雖然介面簽名改掉了,但是本質並沒有變化,還是使用的二段構建。二段構建並沒有什麼不好,只是更加突出了物件需要初始化。在某種程度上也可以說是一種設計強化。因為忘記初始化是一切莫名其妙的bug的罪魁禍首。同時,二段構建出來的物件都是autorelease的物件,而autorelease物件是使用引用計數來管理記憶體的。客戶端程式設計師在使用此介面建立物件的時候,無需關心具體實現細節,只要知道使用create方法可以建立並初始化一個自動釋放記憶體的物件即可。
在一點,在《Effective Java》一書中,也有提到。為每一個類提供一個靜態工廠方法來代替建構函式,它有以下三個優點:
1.與建構函式不同,靜態方法有名字,而建構函式只能通過引數過載。
2.它每次被呼叫的時候,不一定都建立一個新的物件。比如Boolean.valueOf(boolean)。
3.它還可以返回原型別的子型別物件。
因此,使用二段構建的原因有二:
1.相容性、歷史遺留原因。(這也再次印證了一句話,一切系統都是遺留系統,呵呵)
2.二段構建有其自身獨有的優勢。
3.使用此模式的優缺點是什麼?
優點:
1.顯示分開記憶體分配和初始化階段,讓初始化地位突出。因為程式設計師一般不會忘記分配記憶體,但卻常常忽略初始化的作用。
2.見上面分析《Effective Java》的第1條:“為每一個類提供一個靜態工廠方法來代替建構函式”
3.除了完成物件構建,還可以管理物件記憶體。
缺點:
1.不如直接使用建構函式來得直白、明瞭,違反直覺,但這個是相對的。
4.此模式的定義及一般實現
定義:
將一個物件的構建分為兩個步驟來進行:1.分配記憶體 2.初始化
它的一般實現如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class
Test
{
public :
//靜態工廠方法
static
Test* create()
{
Test
*pTest = new
Test;
if
(pTest && pTest->init()) {
//這裡還可以做其它操作,比如cocos2d-x裡面管理記憶體
return
pTest;
}
return
NULL;
}
//
Test()
{
//分配成員變數的記憶體,但不初始化
}
bool
init(){
//這裡初始化物件成員
return
true ;
}
|