所遇不良設計(一)

  咱們在設計時,必定要考慮系統的將來的可擴展性,爲將來作好準備,預備接口。如今軟件的設計爲了下降其耦合度,大多將軟件設計成插件模式。程序員

1 擴展的常量

  系統中出現大量的常量,隨着系統模塊的添加,會不斷增長這些常量; 好比說日誌的類型、貨幣的類型等等。而日誌是每一個模塊一定調用的模塊,貨幣在好多涉及到支付的模塊裏面會被調用,當咱們要增長一個類型的時候,每每牽涉到其餘模塊,當編譯的時候,其餘模塊也要相應的從新編譯。增長一個宏,花了半個小時去編譯,在項目中,我看到不少人抱怨。安全

   我遇到的問題(貨幣的常量):ide

  enum CURRENT_TYPE{
      GOLD_TYPE;
      .
      .
      .
      COIN_TYPE;
      ...
  }
View Code

  我認爲,將要擴展的常量丟到配置文件裏面,這樣可能在速度以及存儲上帶來額外的開銷。我以爲對系統性能沒多大的影響,配置文件通常在系統開啓時加載好。函數

  "gold"=0
  .
  .
  .
  "coin"=n
  ....
View Code

  將上述配置加載到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);
View Code

  咱們只須要不斷的繼承Base而且從新寫Func就能給Interface換上新的「器械」了,這樣子不用更改底層的代碼,致使從新編譯。3d

  C++的第二種模式:指針

  template <class T>
  void Interaface(T* p_t, Arg* p_arg){ //這是接口
      p_t->Func(p_arg);
  }
View Code

  只要寫一個類型而且實現Func函數,不須要繼承任何東西,就給Interface換上新的「器械」了,一樣也減小了編譯時間。

  總結:上述C++的兩種方法,我更加傾向於運用多態,運用模板,你就必須把你的類申明和實現所有丟到頭文件裏面,這樣子發佈API的時候,別人會看到你的實現,並且頭文件會顯得臃腫。

  咱們再來講說C的實現,C的實現很簡單,就是傳送相同的函數指針。

  void Interface (T (*) (Arg* p_arg))
View Code

  這樣子,你只須要在外部重寫這個函數傳進去,就OK了,函數名字能夠不同,可是類型不同,也是一種很酷的方法。其實這種方式咱們在STL的一些庫函數裏面常常看到,好比遍歷:

  void Traverse (iterator<T> beg, iterator<T> end, void (*oper) (T& a)); 
View Code

  只須要變換oper,咱們能夠對容器裏必定範圍內的元素進行顯示,變化他們的值等。

3 強勢的friend

  哥們你要調用我,得要先申請friend,一副盛氣凌人的樣子。我要說可能每一個人負責某個模塊,誰會料到某人的離職或者生病了,的確每一個人寫的模塊,其本人是最清楚的。整個項目是整個團隊的,而不是某我的的。有些人喜歡將本身原本應該開放給其餘模塊的API設置爲protected和private; 覺得這樣會很安全,禁止別人調用,我以爲調用又如何了,又不是會涉及到支付以及破壞系統,更況且後臺系統,不會把API暴露給外部,只是本系統內部使用。何況爲了保護本身的API,把其設置爲protected,我以爲是隔靴搔癢。看看我下面是如何破解,哥們我不求你把我當作friend:

  底層系統類A,咱們須要調用其Func。

  class A {
  protected:
      void Func() {
          ....
      }
  }
View Code

  咱們的破解招數以下:

  class B:public A {
  public:
      void Func(){
          A::Func();
      }
  }
  
  B b;
  b.Func()
View Code

  雖然這樣子繞了一個圈子,不如直接調用來得好,咱們仍是沒有作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;
      }
      ...
  }
View Code

  首先,調用接口者,可能沒有去研究所調用的接口吧,還有可能有那種強迫症吧,才如此作的吧。程序員主要是怕系統崩潰了,找到本身的頭上吧。若是應要判斷空,應該整個系統來約定一下,最上層來判斷或者最底層判斷。其餘時候不判斷的; 我仍是以爲應該由底層來判斷的,這樣子可以讓系統更加健壯。還有我以爲能夠不用返回false的,其實一個函數裏面返回false的因素比較多,返回給外部都不知道里面究竟是什麼緣由致使的false。我以爲作成異常拋出可能更加好一些,不一樣因素致使返回失敗,拋出不一樣的異常,這樣子好跟蹤錯誤。同時這樣子約定,若是該函數不須要處理異常,那也把異常給扔出去,直到誰調用要處理異常,才try…catch。

相關文章
相關標籤/搜索