Go語言 - 清晰,優雅,但卻是錯的
最近國外關于go語言的討論很多,其中有一個論題是關于go語言里采用的錯誤碼的異常處理模式和Java里的try-catch的模式孰優孰劣的問題。今天的這篇文章就涉及到這兩種模式的對比比較。
并不是因為你看不見錯誤的產生就意味著它的不存在。
下面這段代碼來自《C# programming》這本書,摘自講述異常處理的章節。
try { AccessDatabase accessDb = new AccessDatabase(); accessDb.GenerateDatabase(); } catch (Exception e) { // Inspect caught exception } public void GenerateDatabase() { CreatePhysicalDatabase(); CreateTables(); CreateIndexes(); }
你是否注意到了這中寫法是多么的清晰和優雅。
清晰,優雅,但卻是錯的。
假設在執行CreateIndexes()這個方法時有異常拋出。GenerateDatabase()方法并不捕獲它,異常就會拋回給調用者,在那里被捕獲。
但當異常從GenerateDatabase()方法中拋出時,重要的信息就丟失了:數據庫創建的狀態。捕獲到異常的代碼并不知道這創建數據庫的哪一步出的錯。需要刪除掉索引嗎?需要刪除表嗎?需要刪除物理數據庫嗎?它不知道。
如果是在執行CreateIndexes()時出了錯,你就永遠丟失了一個物理數據庫文件和里面的表。(因為這些文件會存放在硬盤上,它們存在哪里并不確定。)
在一個異常拋出模式的編程語言里寫出正確的代碼給人的感覺會更難,相比之下,在一個錯誤碼模式的編程語言里給人的感覺會容易些,因為前者中任何東西都可能拋出異常,你必須時刻準備捕捉它。而后者很顯然,當你在接收到錯誤碼時才去檢查發生的錯誤。在異常拋出模式中,你必須意識到任何地方都可能出現異常。
換句話說,在錯誤碼模式中,如果有人沒有捕捉到錯誤的發生,很顯然是他沒有檢查錯誤碼。但在異常拋出模式中,你不能很直觀的從代碼中發現是否已經有人捕捉到了錯誤,因為能產生錯誤的地方并不明顯。
思考下面的代碼:
Guy AddNewGuy(string name) { Guy guy = new Guy(name); AddToLeague(guy); guy.Team = ChooseRandomTeam(); return guy; }
這個函數創建了一個新Guy,把他加入到俱樂部里,然后給他隨機分配一個組。 不能再簡單的操作了。
請記住:任何一行代碼都可能產生錯誤。
- 如果”new Guy(name)”拋出了異常,會怎樣?
- 哦,很幸運,我們還沒有開始做什么,沒有損失。
- 如果”AddToLeague(guy)”拋出了異常,會怎樣?
- 我們新創建的“guy”就會被遺棄,但GC會把他清理干凈。
- 如果”guy.Team = ChooseRandomTeam()”拋出了異常,會怎樣?
- 哦偶,我們有麻煩了。我們已經把這個guy加入了俱樂部。如果有人捕獲到了異常,他會發現俱樂部里的這個人不屬于任何組。如果有一段代碼是來遍歷 俱樂部的所有會員,發現這個guy,哪個組的?這時就會得到一個NullReferenceException異常。因為他的組還沒有初始化。
當你在寫代碼時,如果每行代碼都會拋出異常,你是否明白每個異常都會產生什么樣的后果?如果你想寫出正確的代碼,你就需要考慮到這些。
ok,如何修補這個問題?重新組織一下操作步驟。
Guy AddNewGuy(string name) { Guy guy = new Guy(name); guy.Team = ChooseRandomTeam(); AddToLeague(guy); return guy; }
看起來是一個很微小的改動,但卻對錯誤恢復產生巨大的影響。通過延后提交數據(把guy加入俱樂部),在構造這個guy過程中發生任何異常都不會產生任何的后續影響。會發生的事只是一個未構造完成的guy被丟棄了,最終會被GC清理掉。
通用設計原則:除非已經完備,不要提交數據。
當然,這個例子非常簡單,因為在創建guy的步驟中沒有其它的關聯影響。如果在創建過程中出了什么問題,丟掉它就行了,讓GC處理余下的事情。
在現實生活中,情況會麻煩的多。看看下面的代碼:
Guy AddNewGuy(string name) { Guy guy = new Guy(name); guy.Team = ChooseRandomTeam(); guy.Team.Add(guy); AddToLeague(guy); return guy; }
跟上面我們改正過的函數一樣,只是有人認為,如果在組里保持一個成員列表的引用會更有效率些,于是,你需要把自己add到你想加入到組里。這樣做又會產生什么樣的后果?
[本文英文原文鏈接:Cleaner, more elegant, and wrong ]
來自: 外刊IT評論 http://www.aqee.net/