天天看點

程式設計不是功能實作了就可以了

       最近在項目中發現一個事件,功能很簡單,也達到了預期的功能效果,但是程式設計不僅僅是功能實作了就可以了,更應該是如何完美的實作功能。下面我将這個事件的代碼貼出來,進行一下分析。代碼不是很長,總共才19行,該事件是确定按鈕對象上面的一個事件定義。每點選一次确定就會觸發一次該事件。

1   string    ls_error,ls_zyhm

2   sql_lis = create Transaction

3   uo_support.set_trans_object(sql_lis)

4   IF NOT uo_support.dbconnect(2) THEN  return false

5   ls_zyhm = string(istr_jsxx.jsbr.zyh)

6   gf_begin_transaction(sql_lis)

7   update lis_requisition_info set cypb = 8 where inpatient_id=:ls_zyhm and patient_type=1 using sql_lis;

8   if sql_lis.sqlcode = 0 then

9       gf_commit_transaction(sql_lis)

10     if isvalid(sql_lis) then destroy(sql_lis)

11  else

12     ls_error = "更新:lis_requisition_info表失敗!!~r~n"+sql_lis.sqlerrtext

13     gf_rollback_transaction(sql_lis)

14     if isvalid(sql_lis) then destroy(sql_lis)

15      gf_write_log(ls_error)

16      is_errtext = is_errtext + ls_error

17      return false

18   end if

19   return true

問題一:sql_lis的作用域與建立

       很多人第一眼看到該代碼可能覺得sql_lis沒定義,首先該代碼實作了預期的功能,并且能夠運作,說明sql_lis定義了,大家普遍覺得sql_lis沒定義是有原因的,因為sql_lis的建立與銷毀完全是按照局部變量來實作的,是以大家一眼就覺得sql_lis是局部變量,但是在該項目中sql_lis是被定義成了全局變量。

       何為全局變量?我一直持有一個觀念,就是全局變量在系統開始之初就被指派,系統的運作過程中不能再對全局變量進行重新指派。這個是有原因的,全局變量使用起來非常的靈活,全局變量是供各個子產品共同使用的變量,如果每個子產品都随意按照自己的需要對全局變量進行重新指派,會對使用該全局變量的子產品造成很嚴重的影響。

問題二:uo_support對象的事務屬性指派

       如果不熟悉系統架構,可能看不出這個問題,事件第三行對uo_support對象的事務屬性進行了指派,在此sql_lis作為一個引用參數被傳入到uo_support對象中,當後面uo_support調用dbconnect方法時,sql_lis就連上了資料庫。當然,uo_support對象的事務屬性也變成了sql_lis,緊接着後面又把sql_lis給銷毀了,後面無論是修改密碼還是登出對資料庫的操作都使用了sql_lis,造成事務對象不可用,當然了即便不銷毀,uo_support裡面的操作使用sql_lis也是錯誤的(表跟資料庫不比對)。

問題三:sql_lis斷開連接配接

       sql_lis對象銷毀之前,沒有将資料庫連接配接斷開,本身該事件就是點選确定按鈕的時候調用的,操作非常頻繁,資料庫連接配接不斷開,勢必會造成資料庫會話的不斷上升,直到系統退出資料庫才會釋放該程序占用的所有會話。也許有人會反駁了,如果資料庫會話不斷上升的話,資料庫連接配接應該會被占滿才對啊。大家都知道pb開發出來的系統是單線程運作的,重複相同的操作,很大可能會造成sql_lis使用了相同的記憶體塊,在這種情況下,資料庫端是不會重複開辟會話而是直接使用了上次沒有釋放的會話。我特意進行了測試,調用完該事件之後,點選另一個按鈕故意建立一個對象不釋放記憶體,這樣再次回過頭來調用上面事件的時候,由于新建立的sql_lis被配置設定到了新的記憶體塊,造成資料庫端重建立立了一個新的連接配接,造成資料庫端session的上升。

問題四:sql_lis連接配接到底應該放在什麼地方

       在這個事件中,sql_lis的連接配接放在了事件當中,但是該事件是頻繁被調用的,這樣的後果就是sql_lis不斷的被建立,不斷的連接配接資料庫,不斷的銷毀sql_lis.大家都知道tcp建立連接配接是三次握手,斷開連接配接是四次握手。也就是說在不斷的操作過程中,注意我們的系統是成百上千的用戶端在同時使用的,并不是單機系統。tcp的建立連接配接與斷開連接配接是非常占用網絡帶寬的。生産環境中,資料庫的配置是不會變的,也就是說資料庫一旦建立連接配接,在網絡沒有斷開的情況下是一直可以使用的,完全沒必要在局部操作中去建立資料庫連接配接。這也是為什麼基本所有系統都是剛開始啟動時連接配接上資料庫,後面系統的運作過程中一直使用的原因。當然了有的系統在使用的過程中也會判斷一下連接配接是不是由于斷網等原因造成不可再用,在這樣的情況下,是可以重新進行資料庫連接配接的。

問題五:資料庫連接配接失敗的時候直接傳回

       第四行代碼可以看到,sql_lis連接配接失敗的時候直接傳回了false,此時雖然sql_lis連接配接資料庫失敗,但是sql_lis對象本身已經建立了,也已經配置設定記憶體了,傳回之前需要先銷毀sql_lis對象。也許有人會說了,代碼都執行完了,sql_lis對象不是會自動銷毀嗎?前提是sql_lis在此處是全局變量,事件結束了,sql_lis的生命周期并沒有結束,作為全局變量,他是不會自動銷毀的。當sql_lis網絡斷掉後,其他資料塊網絡如果正常,頻繁的調用該事件,就會不停的建立sql_lis,而且建立完了又不銷毀,這樣的話,很快記憶體就會洩露幹淨了。

       短短的19行代碼,暴露出了五個非常嚴重的問題,程式設計不是兒戲,實作功能是前提,如何更好的實作是重點。當客戶不停抱怨的時候,當程式bug給客戶帶來利益損失的時候,我們是否能夠靜下心來自我反思一下。任務重,時間緊不是借口,産品的健壯與否關系到公司乃至所有同僚的利益。日常的工作中,養成良好的程式設計習慣,為産品的健壯做出程式員該做的貢獻吧。