Table of Contents
咱們在設計時,必定要考慮系統的將來的可擴展性,爲將來作好準備,預備接口。如今軟件的設計爲了下降其耦合度,大多將軟件設計成插件模式。程序員
1 擴展的常量
系統中出現大量的常量,隨着系統模塊的添加,會不斷增長這些常量; 好比說日誌的類型、貨幣的類型等等。而日誌是每一個模塊一定調用的模塊,貨幣在好多涉及到支付的模塊裏面會被調用,當咱們要增長一個類型的時候,每每牽涉到其餘模塊,當編譯的時候,其餘模塊也要相應的從新編譯。增長一個宏,花了半個小時去編譯,在項目中,我看到不少人抱怨。安全
我遇到的問題(貨幣的常量):ide
enum CURRENT_TYPE{ GOLD_TYPE; . . . COIN_TYPE; ... }
我認爲,將要擴展的常量丟到配置文件裏面,這樣可能在速度以及存儲上帶來額外的開銷。我以爲對系統性能沒多大的影響,配置文件通常在系統開啓時加載好。函數
"gold"=0 . . . "coin"=n ....
將上述配置加載到hashmap中去,這樣子在調用的時候使用hashmap["gold"]來獲取其類型。有人(多是一個acmer)就會說這沒有宏高效,的確如此啊; 可是貨幣的數量畢竟是少的,這點速度犧牲不算什麼,換來了靈活的可擴展性。性能
註明: 一些結構不復雜的常量設置,我並非很喜歡xml,xml的表現太過複雜; 咱們能不能選擇像httpd.conf(Apache的配置文件)那樣簡單明瞭。這可能也只是個人一廂情願罷了,首先遊戲的模板數據比較複雜,涉及到嵌套,這樣簡單的屬性配置文件是表現不出數據關係的。同時爲了達到統一性,最終會仍是xml比較好。spa
2 多變的函數
有時候,咱們要調用上乘的接口,發現沒有咱們要調用的接口,必需要到上層模塊裏面添加適合咱們業務的接口; 每遇到這種類似的業務,都要如此。這就說明上層的設計不夠完美。要想一想咱們要讓機器人可以使用錘子、鉗子 扳手,咱們是否是要拆卸它的手臂,給它換一個手臂呢? 這樣很麻煩,咱們何不爲其設計接口,給它換上什麼器械,它就能幹不一樣的活。在C++裏面使用多態,也可使用模塊; 在C裏面就是傳送同一類型的函數指針。插件
C++的第一種模式:設計
//底層 class Base { virtual T Func (Arg* arg); }; void Interface (Base* a, Arg* p_arg){ //這是接口 a->Func(p_arg); } //擴展 class ABase : public Base { virtual T Func (Arg* p_arg); } ABase* pABase; Interface(pABase, p_arg);
咱們只須要不斷的繼承Base而且從新寫Func就能給Interface換上新的「器械」了,這樣子不用更改底層的代碼,致使從新編譯。3d
C++的第二種模式:指針
template <class T> void Interaface(T* p_t, Arg* p_arg){ //這是接口 p_t->Func(p_arg); }
只要寫一個類型而且實現Func函數,不須要繼承任何東西,就給Interface換上新的「器械」了,一樣也減小了編譯時間。
總結:上述C++的兩種方法,我更加傾向於運用多態,運用模板,你就必須把你的類申明和實現所有丟到頭文件裏面,這樣子發佈API的時候,別人會看到你的實現,並且頭文件會顯得臃腫。
咱們再來講說C的實現,C的實現很簡單,就是傳送相同的函數指針。
void Interface (T (*) (Arg* p_arg))
這樣子,你只須要在外部重寫這個函數傳進去,就OK了,函數名字能夠不同,可是類型不同,也是一種很酷的方法。其實這種方式咱們在STL的一些庫函數裏面常常看到,好比遍歷:
void Traverse (iterator<T> beg, iterator<T> end, void (*oper) (T& a));
只須要變換oper,咱們能夠對容器裏必定範圍內的元素進行顯示,變化他們的值等。
3 強勢的friend
哥們你要調用我,得要先申請friend,一副盛氣凌人的樣子。我要說可能每一個人負責某個模塊,誰會料到某人的離職或者生病了,的確每一個人寫的模塊,其本人是最清楚的。整個項目是整個團隊的,而不是某我的的。有些人喜歡將本身原本應該開放給其餘模塊的API設置爲protected和private; 覺得這樣會很安全,禁止別人調用,我以爲調用又如何了,又不是會涉及到支付以及破壞系統,更況且後臺系統,不會把API暴露給外部,只是本系統內部使用。何況爲了保護本身的API,把其設置爲protected,我以爲是隔靴搔癢。看看我下面是如何破解,哥們我不求你把我當作friend:
底層系統類A,咱們須要調用其Func。
class A { protected: void Func() { .... } }
咱們的破解招數以下:
class B:public A { public: void Func(){ A::Func(); } } B b; b.Func()
雖然這樣子繞了一個圈子,不如直接調用來得好,咱們仍是沒有作friend,就獲取了A的資源; 要作到保護,作絕點,應該就是private了。哥們要放低架勢,要是好多模塊都要調用你的模塊,每次要在你的頭文件裏面加入friend,會致使大量從新編譯的。這也是我討厭friend的緣由,要作咱就作好基友或者好閨蜜。
4 層疊的工程
作項目的過程當中,你們確定遇到過這樣問題,就是遇到指針都要判斷空,造成了強迫症,就是怕訪問到空指針,致使系統崩潰的。即便是底層已經幫咱們作了,我仍是要判斷。咱們先來看看情形吧。
bool Func1 (Player* pPlayer, ...) { if (NULL == pPlayer) { return false; } ... } bool Func2 (Player* pPlayer, ...) { if (NULL == pPlayer) { return false; } ... if(false == Func1(pPlayer, ....) { return false; } ... } bool Func3 (Player* pPlayer, ...) { if (NULL == pPlayer) { return false; } ... if(false == Func2(pPlayer, ....) { return false; } ... }
首先,調用接口者,可能沒有去研究所調用的接口吧,還有可能有那種強迫症吧,才如此作的吧。程序員主要是怕系統崩潰了,找到本身的頭上吧。若是應要判斷空,應該整個系統來約定一下,最上層來判斷或者最底層判斷。其餘時候不判斷的; 我仍是以爲應該由底層來判斷的,這樣子可以讓系統更加健壯。還有我以爲能夠不用返回false的,其實一個函數裏面返回false的因素比較多,返回給外部都不知道里面究竟是什麼緣由致使的false。我以爲作成異常拋出可能更加好一些,不一樣因素致使返回失敗,拋出不一樣的異常,這樣子好跟蹤錯誤。同時這樣子約定,若是該函數不須要處理異常,那也把異常給扔出去,直到誰調用要處理異常,才try…catch。