何時使用異常

先說個題外話: 在公司作了倆件事, 是我以爲頗有意義的, 第一就是成立了一個PHP郵件組, 第二就是成立了一個Hi羣. 目前倆者都有超過500 phpers在裏面. 我一直認爲, 構建一個交流平臺, 讓同窗們能順暢, 簡單的溝通, 是營造積極的技術學習氛圍的基礎和前提. 讓每一個人的問題不會成爲別人的問題, 則是最直接的利益. (後記: 很多人都問郵件組地址, 實在很差意思, 這個郵件組是公司內部的郵件組, Hi也是公司內部的. 謝謝)php

昨天, 有同事在郵件組提了個問題:html

PHP應該何時使用 Exception ? 它的性能如何?設計模式

這個問題也算是一個久經爭論的經典問題了. 我談談個人我的見解.數組

異常與之對應的錯誤碼(或者狀態碼), 到底各自有什麼優勢, 缺點, 咱們應該怎麼使用呢?函數

錯誤碼

首先來講, 異常機制是在錯誤碼機制以後纔出現的, 那麼根據進化論, 異常天然是避免了錯誤碼機制的一些不足. 這些不足包括.性能

1. 錯誤信息不豐富

函數, 只能有一個返回值(固然, Lua能夠返回多個, 但其實也至關於在PHP中返回一個數組), 咱們見過最多的函數說明就是: 成功時候返回***, 錯誤的時候返回FALSE, 然而一個函數出錯我緣由可能有多種, 出錯的種類更有多種. 一個簡單的FALSE, 並不能把具體的錯誤信息告訴調用者.學習

因而, 咱們也就見過一些, 這樣的函數說明: 若是返回值大於0, 則表示成功的狀態碼, 若是返回值小於0, 則表示出錯的狀態碼.spa

然而, 這個要求函數是返回整形(或者數字), 對於一些其餘函數, 咱們並不能經過0, >0, <0來判別, 而且, 即便經過這樣的方式, 咱們還須要用返回的錯誤碼和一些預約義宏(或者調用相似strerror())來獲取具體的, 可讀的錯誤信息..net

因而, 就有一些函數使用全局的錯誤碼, 和錯誤信息, 來保存具體的錯誤信息, 這個時候咱們就看到這樣的函數描述: 成功返回***, 出錯的時候返回FALSE, 錯誤代碼保存在全局變量$errno中(至少大多數Linux庫函數是這樣描述的, 呵呵).設計

Okey, 這樣的方式確實能夠工做, 可是, 是否是以爲, 很醜陋呢?

2. 加入錯誤狀態碼可能須要改變函數簽名

假設, 你編寫了一個函數, 這個函數很簡單, 很簡單, 你認爲他絕對不會出錯, 因而你申明爲(用C語言爲例, PHP沒有返回類型提示):

  1. void dummy() {
  2. }

可是後來你慢慢修改了這個函數, 給了它更多的功能, 此時這個函數可能會失敗了. 而你如今根本沒法爲這個函數, 加入錯誤返回碼了.

也許有人說PHP沒有返回值類型限制一說, 可是想一想PHP的構造函數, 構造函數是沒有返回值的, 當發生錯誤的時候, 若是你不使用異常, 我想你只能選擇die, 或者使用2中的方法來錯誤繼續執行了.

另外, 在一個良好的軟件系統中, 返回類型其實也是約定俗成的, 當全部的使用的函數的地方, 都沒有檢查返回值的時候, 你仍是沒法爲這個函數加入錯誤返回碼.

3. 錯誤狀態碼可能會被忽略

當你的一個函數, 出錯了, 返回了錯誤狀態碼, 而調用方並無檢測這個返回值, 會發生什麼狀況呢? -_#. 令一方面, 到處檢測返回狀態碼, 會形成代碼很是的,,ugly:

  1. <?php
  2.   if (!call1()) {
  3.       die();
  4.   }
  5.  
  6.   if (call2() != SUCCESS) {
  7.      die();
  8.   }
  9.  
  10.   if (call3() < 0) {
  11.       $msg = error_get_last();
  12.       die($msg["message"]);
  13.   }

異常機制

那麼如今咱們來看看異常機制, 若是咱們採用異常機制, 上面的代碼能夠寫做:

  1. <?php
  2. try {
  3.    call1();
  4.    call2();
  5.    call3();
  6. } catch (Exception $e) {
  7.    die($e->getMessage());
  8. }

更方便的, 若是你的代碼只是中間層, 你的調用方會負責處理錯誤的話, 你甚至能夠簡單的寫做:

  1. <?php
  2. function myFunc() {
  3.    call1();
  4.    call2();
  5.    call3();
  6. }

而一個異常對象, 能夠包含更豐富的錯誤信息, 好比錯誤信息, 錯誤碼, 錯誤的行數, 文件, 甚至出錯上下文, 等等, 避免的」1.錯誤信息不豐富」的不足.

咱們也能夠爲一個返回void類型的函數增長異常, 而不改變他的函數簽名, 也就不會有上面說的」2.加入錯誤狀態碼可能須要改變函數簽名」. 對於PHP來講, 若是咱們新加入的錯誤沒有被捕捉, 也不用擔憂, 會明顯的出錯的. 也就不會發生上面所說的」3. 錯誤狀態碼可能會被忽略」的狀況.

然而, 也有一些反對使用異常的聲音:

1. 性能

正如文章開頭提問中的: 「它的性能如何?」, 異常機制確實要比返回狀態碼的方式昂貴一些, 對於C++來講, 在異常發生的時候, 還要發生堆棧解退(對於PHP來講, 沒有這個邏輯, 具體的你們能夠參看我之間寫的一篇文章: 深刻理解PHP原理之異常機制).

性能和方便, 每每是一個矛盾體, 我只能說, 你須要權衡, 若是你寫的是一個小的模塊, 而且它的生命期可能很短, 也不須要什麼特殊的設計模式, 那我以爲你能夠不用異常.

而若是你在爲一個龐大的軟件作開發, 我想你更應該看重的, 應該是, 它的可擴展性, 可維護性.

2. 太多可能的Uncaught Exception

若是, 你調用了一個可能發生異常的函數, 可是卻沒有捕獲這個異常, okey, Fatal Error了, 因此讓咱們的代碼看起來:

  1. <?php
  2. try {
  3. } catch () {
  4. }
  5. ....
  6.  
  7. try {
  8. } catch () {
  9. }
  10. ....
  11. try {
  12. } catch () {
  13. }

然而, 這個是能夠通過良好設計避免的, 好比我在設計Yaf的時候, 就提供了全局異常處理, 也就是相似於, 你在最最頂層, 加上了一個try catch, 全部的異常錯誤邏輯都加到這個裏面, 你也能夠很方面的把你本身的異常加進去.

結論

常常有人批評我是倆面派, 呵呵, 可是在你們瞭解了上面的利弊之後, 是否也會和我同樣認爲: 這個事情沒有定論呢? 一切從實際出發. :)

說了這麼多, 算是拋磚引玉, 歡迎補充

相關文章
相關標籤/搜索