NO IMAGE

軟體開發中遇到的一些問題

 

對近日來一些問題進行思考,希望能有個解決方案。

 

1、資料庫方面

每個專案都離不開資料庫,而資料庫的建立過程是個問題,如何將我們的開發成果移動到運營環境中是個問題,如何維護以後的更新更加是個問題,所有的東西都看起來是那麼簡單而缺乏技術含量,但真的嘗試把它做好卻是非常不容易,人工手動來維護這些文件是可以的,但恐怕這是一個專門的工作,如果幹這個的人還同時做別的事情,總會無法避免地出現疏漏,我想一定需要藉助工具了,而與這些工具的磨合,也是個不小的成本。

 

1)往往忘記寫修改履歷,不知道哪個是新的,哪個是舊的

通病了,希望修改履歷都別忘了寫,包括修改日期、作者和內容,這樣就不至於到出了問題找人負責的時候手忙腳亂,至於用CVS來管理怎樣,我沒經驗。

 

2)多個人同時直接修改資料庫,管理這個的人很痛苦

沒有好的解決方案。用CVS來維護如何?

 

3)Oracle 9i開發出的程式不一定能在Oracle 8i上使用

千萬別使用高版本的開發環境和低版本的運營環境,理想情況是一樣的環境,不行的話反過來用低版本的開發環境和高版本的運營環境也可以。切記,產品的成功不在於開發它的工具是高階還是低階。

圖1

由於版本不一致,原先設計好的註釋到新的環境就變成了問號,不堪忍受

 

4)外來鍵約束使用不當導致很多問題

外來鍵約束很大程度上保證了資料的完整性,但世界上所有的事情都是一分為二的,在我們開發過程中,外來鍵約束往往約束的是我們而不是資料,當我們需要新增一行測試資料的時候,當我們需要修改一個欄位屬性的時候,當我們需要更新一個單元格的時候……到最後,我發現我們的專案去處了很多外來鍵約束,最後的成果和設計相去甚遠了。對於小型專案,很多時候沒必要使用外來鍵約束,當你確定真的需要使用的時候,仔細思考,它真的合理嗎?

 

5)冗餘與效率問題

總結過程中我發現了一些資料冗餘,這是很正常的,比如一個使用者表中包括了使用者性別、出生日期和身份證號碼,其實這就是冗餘,因為我們完全可以通過身份證號碼來確定使用者性別和出生日期,但這種冗餘是必要的。一定的冗餘能提高我們的效率,我們往往需要在冗餘與效率之間選擇一個平衡點。參考資料庫三正規化。

 

6)命名問題

表面上看又是一個沒有技術含量的小問題,其實是最令我頭疼的問題之一,一張user表中,出現了多個“state”,state表示一種狀態,每個使用者有若干種狀態,很正常,我稍微列一下我能看到的:super_assistant_state、login_state、multi_user_state、forbidden_state、lock_state、score_lock_state……如果和別的表關聯起來,恐怕還有更多的state,這些state如果命名得不好,往往容易引起誤會,比如lock_state和forbidden_state,兩個都表示對使用者的限制,如果註釋中沒有進一步詳細的說明,誰又會知道那麼多呢?

 

7)型別混亂的問題

欄位的型別有時候看起來確實混亂,比如什麼時候用integer,什麼時候用number,我想這絕對不是隨意的,至於number究竟有多長,最長可以指定多長?誰研究過呢?integer和C 中32位的int型別有什麼異同嗎?而long型別和C 的long是一樣的嗎?字串到底用char、varchar還是varchar2?我想需要進一步研究。

 

8)過於複雜的單句SQL語句

Oracle的功能是很強大的,幾乎支援所有的SQL風格,當然包括了複雜的聯合查詢和子查詢,但過於龐大的select語句使後來的維護者感覺非常費勁,一個語句往往需要閱讀很長時間,而且根據我的經驗,除錯時候出問題多的儲存過程就是包含了這些複雜SQL語句的儲存過程,我們有辦法將它簡化嗎?如果不能簡化,務必謹慎,測試測試再測試,確保其正確性,然後新增清晰的註釋。

 

2、應用程式方面

應用程式的問題主要還是集中在版本的控制上,包括訊息標頭檔案的版本,運營與維護的不同版本程式,測試與發行的不同版本。當然還有別的問題,不一定是技術上的原因了,使得後來做出來的成品和原先設計的相差很大,很多設計文件實際上已經廢棄,加大了以後維護的難度。

 

1)common_lib的放置

common_lib是我們使用的一套公共類庫(與之類似的還有aeslib),主要的功能是網路通訊、寫log、hash表和佇列等。幾乎所有的程式都用到了這些功能,不考慮common_lib本身版本上的差距,我們有不同的使用方法,以Linux環境開發為例,一種是把common_lib與專案分離,放置在$(HOME)目錄下,而在make檔案中也指定了“$(HOME)/common_lib”的搜尋路徑;另一種是把common_lib放置在專案目錄中,make檔案指定的路徑可能是“../common_lib”。前一種方法有可能把專案從一臺機器移動到另一臺機器(或從一個賬號移動到另一個帳號)的時候發現common_lib找不到的情況,因為“$(HOME)/common_lib”不一定有嘛;後一種方法有可能會將common_lib所作的一些額外更改忽略掉,它用的還是自己的庫。但考慮到common_lib趨於穩定,因此我們儘量使用後一種方法,程式能正確,穩定地執行就是我們的追求了,最新的程式碼並不是必須的。另外,使用common_lib的同時,我們也通常會用到aeslib,我覺得是不是將它們合併起來更好呢?

 

2)程式中的錯別字

這是個不重要又重要的問題,也許大家都預設之後不會覺得有什麼不妥,但站在使用者的角度,會不會覺得開發人員水平很差呢?我列舉一下常見錯誤(括號內是正確用法):登陸(登入)、Acount(Account)、超連線(超連結)、Sucess(Success)。

 

3)資料庫?配置檔案?寫死?

我們可能需要修改一些程式執行的引數,比如開分最大金額、心跳時間、最大連線客戶端數目……等等。這些引數的改變究竟如何實現?我觀察了下程式,普遍有三種情況,一是將引數存放在資料庫中(可以在執行中生效),二是寫配置檔案(重新啟動程式生效),三是寫死在程式中(需要重新編譯程式,再執行方生效)。這個除了看需要外,我還想提些建議:1、如果需要網頁方面控制,只能使用資料庫了;2、只要以後有可能需要修改就不要寫死在程式中,要知道,編譯的環境不是哪裡都有,就算有也不是什麼人都會,況且程式碼是保密的;3、對於較多的批量配置資料,儘量使用資料庫;4、程式初始化的配置資料使用配置檔案通常更為恰當,因為初始化好之前往往無法訪問資料庫嘛。最後,貪方便把東西都寫死是不負責任的表現,結果往往帶來很多不方便。

 

4)版本依然混亂

我經常說的一句話是:“XXX,請把YYY的最新版本程式碼,給我一份。”或者說:“XXX,你這個是最新版本嗎?最近一次改了什麼內容?”其實老說這句話我都覺得丟臉,做了這麼久開發,版本控制問題還是搞不好,不排除制度和開發模式上也有些問題,但考慮到自己有時候都不能很好執行,就不用怪別人了。通常表現為:1、修改隨意,常常忽略修改標識,無日期,無內容,改錯了回頭再尋找困難;2、經常忘記CVS檢入前先同步一次,導致內容混亂;3、責任不明確,程式到底誰在負責啊?比如有人離開了公司,他的程式碼究竟誰來管?4、舊版本經常不留備份,修改過程無蹤跡可尋(CVS可能經歷很多修改才檢入一次)。

 

5)目錄結構安排

一個工程,安排怎樣的目錄結構?單個目錄?或者許多?我想這應該不是隨意的,我認為通常可以這樣:將公共模組放置一個目錄,將類庫(比如資料庫操作的類庫,圖形類庫,加密狗類庫)放置各自的目錄,剩下自己編寫的程式碼放一個目錄即可,但如果自己寫的程式碼模組獨立性強,也可以考慮把他們分開,以便以後的複用。還有就是bin目錄的建立,現在想想還是有必要的,將生成的可執行檔案放置bin目錄下(VC 自己有debug目錄和release目錄就另外講了),配置檔案也放置bin目錄下,釋出時候只需要釋出bin目錄嘛,我們通常寫log,log目錄呢?我認為放置在bin目錄下,這樣釋出的時候也沒忘記帶上log目錄,當然啦,要先將裡面的log檔案清空。

 

6)系統設計的問題

在做概要設計的時候,我們往往有很多不錯的想法,比如構建一個比較完美的遊戲平臺,以後只需要在平臺上新增各種不同的遊戲即可,這樣就產生了對應的不同資料庫,平臺自身一個資料庫,每個遊戲都有自己的資料庫,理論上沒問題,實際操作起來問題就大了。先是web方面根本沒考慮過這種情況,只設計了一個資料庫連線,之後重新新增了新的資料庫連線,但可擴充性恐怕就不好了,遠沒達到我們期預的效果,再就是控制管理部分程式權力過大,或者說設計不合理,往往逾越了平臺和具體遊戲之間的鴻溝,進一步加強了偶合,使平臺和遊戲越發不可分離,擴充套件性更差,最後做出來的產品已經很難把平臺和遊戲區別開來了,一個平臺就是一個遊戲,一個遊戲包括一個平臺。

 

7)考慮上的疏漏

舉個例子吧,我們在實際操作中需要建立新的伺服器,但發現不成功,查原因,發現是因為資料庫裡需要新增新的伺服器的條目才可以,新增條目是web的功能,結果發現web沒做好,等web補上了,條目新增了,發現還是不行,因為伺服器的執行需要資料庫的很多資訊引數,而這些引數目前都沒有在新增條目的時候被新增,由於這次web的工作量較大,一時改不好,只能手動在資料庫裡新增,一張表新增的條目可能有數十條,相當繁瑣,稍微不留神就可能出錯。考慮上的疏漏可能會伴隨我們一生,但每一點一滴都是寶貴的經驗,起碼我們不能再犯同樣的錯誤。

 

8)log檔案處理

程式離不開log,按照我們的做法,每天產生一個log檔案,時間一長,log檔案就越來越多,佔用空間越來越大,我想我們應該改進一下,比如每天自動刪除3個月以前的log。

 圖2

一天加一天,log氾濫成災:)

9)exit的使用

程式碰到異常情況我們通常喜歡用exit()來結束程式的執行,在單執行緒中這樣做是沒問題的,但到了多執行緒則未必,根據我的經驗,濫用exit()很容易導致程式結束的時候出現“非法操作”,甚至資料庫寫入不完整。下面是我認為的以後的做法(不一定很正確):只有主執行緒能呼叫exit(),其它執行緒執行遇到致命錯誤後返回錯誤值,一層層往上返回,直至主執行緒,(或者將致命錯誤訊息發至主執行緒)由主執行緒呼叫exit()。當然主執行緒也可以完全不用exit(),我更偏向於能不用就不用,因為exit()會不分黑紅皁白強制結束程式,它不能讓物件正常釋構,另外它有類似goto語句,破壞程式的結構化,使程式條理變得不清晰。

 

10)多執行緒中MsgBox的問題

測試/維護過程中發現過一些很奇怪的現象,程式莫名其妙地到了其它電腦就出現“非法操作”,後究其原因發現是使用MsgBox(這裡通指訊息框)的問題,該呼叫會彈出對話方塊請求使用者確定取消等,或者僅僅將一些訊息反映給使用者。在多執行緒中使用MsgBox我認為存在隱患,一方面是開發工具的問題,MsgBox在C Builder中的實現和WINAPI的MessageBox是不一樣的;另一方面MsgBox並不能每時每刻都能工作得很好,我就發現過多執行緒程式中C Builder的MsgBox不起作用的情況;再一方面MsgBox會對執行緒造成堵塞,如果讓邏輯處理執行緒直接呼叫MsgBox則可能導致一些沒有預料到的情況發生。我認為,MsgBox和使用者介面相關,儘量只在主執行緒中呼叫。

 圖3

這種MsgBox恐怕難以令人接受

 

3、文件方面

這裡指的文件包括了各種設計文件、配置檔案、說明書及程式註釋,是程式可維護性的重要依據,但往往容易被忽略,它不能影響程式的效能,但我覺得從現在的角度來說,一個程式的可維護性往往比效能更重要。

 

1)配置檔案的問題

到底把配置檔案放入CVS呢還是不放呢?都各有道理,放的話檢出中有這個檔案,使用者知道去修改,但如果一個使用者修改了配置檔案並檢入,然後另一個使用者更新,那另一個使用者的配置檔案也跟著被改動了,可能導致錯誤;如果不放,那使用者第一次檢出時候沒有這個配置檔案,無法執行程式,但獲得這個檔案後不會因為之後的更新而導致檔案被修改。我看還是不放的好,不經意地被改動配置檔案是件很鬱悶的事情,寧願找不到配置檔案自己另外去找一個。但有沒有其它更完美的辦法?

 

2)安裝配置說明的問題

我第一次把我認為不錯的安裝說明交給測試部讓他們去執行的時候,我說:“儘量參照說明,不要問我,看看是否能按部就班完成。”結果是很令人沮喪的,一天跑來問我許多次,因為實在不知道下一步怎麼弄,或是出了些意外。當然不排除是因為Linux易用性差的緣故,但無論怎麼說,我的說明文件也十分糟糕,我一直在想怎麼才能寫出合格的說明文件呢?我想應該寫好之後,把自己當成一個使用者,嘗試按說明去操作一遍,這是最起碼的了。當然要寫得好,真不亞於程式設計的難度,你考慮過意外出錯嗎?還有各種你看起來平常的術語使用者是否就清楚?仔細仔細再仔細,難道說明真的很完美,沒有任何錯誤了嗎?我沒有進一步的解決,只有苦功,在此提一下這其實並不簡單,僅此而已。

 

3)說明書的問題

先參考一下上面所說的安裝配置說明的問題,是否存在,還有就是以下的一些問題了:1、格式不統一,章節不對齊;2、圖片過大,調整後模糊,影響閱讀;3、內容混亂,針對性不強,到底是一個針對網路管理員的說明書,還是針對一個普通使用者的說明書?我想內容肯定相差很大。