[獨自murmur]謹慎使用全域變數

  • 7933
  • 0

[獨自murmur]謹慎使用全域變數

全域變數的濫用,會造成程式維護上的困難。


根據Code Complete2的Ch5,講到設計耦合性低的系統,應小心使用全域變數,

全域變數會帶來許多程式設計難題。
1. function在操作時,不知道還有其他function也在全域操作。
2. function雖然知道其他也在操作,但是不知道它們進行的操作。


舉例來說,(下面這段code,一樣是腦袋中的虛擬碼,不過我盡量讓它符合設計的格式了)
這個例子是按了Calculate的按鈕,要把畫面上ItemPrice1~ItemPrice3的資料加起來,計算時要符合折扣,然後存入DB。

{

 integer total=0;
 integer discountBound=300;

 private void PaymentTotal(integer vPayment)
 {
  total+=PaymentDiscount(vPayment);
 }

 //超過discountBound打8折
 private void PaymentDiscount(integer vPayment)
 {
  if(vPayment>discountBound)
  {return vPayment*0.8;}
  else
  {return vPayment;}
 }

 //假設UI有限制textbox的格式需為整數且必KEY
 protected void Calculate_Click(object sender, ImageClickEventArgs e)
 {
  PaymentTotal(this.ItemPrice1.text);
  PaymentTotal(this.ItemPrice2.text);
  PaymentTotal(this.ItemPrice3.text);
  SavatoDB();
 }

 //將總金額存入DB
 private void SavatoDB()
 {
  //把total直接存到DB...
 }
}
 

這個例子在計算最後的總金額,是透過全域變數total來當暫存的結果。

 

可以發現,在每個function處理時,其實對每個function自己來說,並不知道是否有其他function會影響到total這個變數
甚至,會出現「要先執行functionA()之後,才能執行functionB(),最後的結果才會符合我們需要的邏輯」這類的情況。
也就是function與function之間出現相依性(耦合變高)。

這對偵錯與維護都會造成很大的不便。

 

今天我存檔之後,發現total錯了,(通常會是「明明之前可以,為什麼現在不行」的情況)
我得花很大的力氣去找「到底那邊改變了我的total」,
一切的原因就在於,我的total是全域變數,我不知道在整個執行過程中,到底被誰改變,到底被誰影響,
或是我這次的增加或修改的程式碼,呼叫的順序錯了等等千奇百怪的原因,都可能會造成我在算總金額時的意外。

 

那全域變數要怎麼用?
小的只是隻沒寫多少程式的菜鳥,所以只能提供自己的一點小經驗。
1.初始化後不再改變,這種可以使用。
2.改用區域變數+參數來寫。

 

第一種情況,因為值assign的動作只有一次,也很有可能是初始化的值,所以不用擔心出問題時找不到地方。
例如,我們整支頁面程式都要讀取到Session("UserID")的值,
那我可能宣告一個全域變數叫userID,在Page_Load()的時候,將Session("UserID")的值assign給userID。
之後要使用Session("UserID")就用userID的值即可。

 

這樣的好處是不必每次都讀取session,且花的effort只是個小小的變數記憶體空間。
之後維護時倘若userID要多額外的判斷條件才能決定,也只需要修改userID初始化的定義即可。

 

第二種情況,
則是在function裡面用區域變數+參數,來取代全域變數的功能。

 

例如

//假設UI有限制textbox的格式需為整數且必KEY protected void Calculate_Click(object sender, ImageClickEventArgs e)
 {
      integer total=0;
  total+=PaymentDiscount(this.ItemPrice1.text);
  total+=PaymentDiscount(this.ItemPrice2.text);
  total+=PaymentDiscount(this.ItemPrice3.text);
  SavatoDB(total);
 }

 //將總金額存入DB
 private void SavatoDB(integer vTotal)
 {
  //把vTotal直接存到DB...
 }

 

如此一來,SavatoDB不會被全域變數影響,
Calculate_Click也不會被全域變數所影響,
且能達到原本需要的功能。
希望這個粗糙的例子,可以表達出我要解釋的概念。


如果你的code裡面,出現了一堆function或void,是沒有參數的,
或void有達到output的功用,卻不需要return值,
請檢查看看,是不是因為使用全域變數來達到這樣的功能。


貪圖「這次」的方便,會造成debug或維護上工時增加的最大原兇。

 

 


blog 與課程更新內容,請前往新站位置:http://tdd.best/