l

2013年11月23日 星期六

2012緬甸考察之旅Day3-E紡織工廠、烏本橋

Nov. 11 17:19~17:56

紡織工廠

離開因瓦之後先被帶去參觀一家紡織工廠,很多布料都還是用半人工的方式織出來的。由於沒有採購的需要,在這裡沒有待很久。

螢幕快照 2013-11-11 下午5.20.46螢幕快照 2013-11-11 下午5.20.59螢幕快照 2013-11-11 下午5.21.11螢幕快照 2013-11-11 下午5.21.20螢幕快照 2013-11-11 下午5.21.32螢幕快照 2013-11-11 下午5.21.41

***

烏本橋

烏本橋(U Bein ),是世界上最長的柚木橋,長1200公尺,橫跨東塔曼湖,很多觀光客來這裡看日落。烏本橋大部分的地方都沒有護欄,而且腳下的許多木板已經鬆動,雖然橋面還算寬闊,但走路還是要注意腳下,以免不小心踩空。

除了烏本橋上走一圈,走到對面參觀附近的佛寺以外,還可以租一艘小船觀賞日落,或是坐在湖邊喝杯飲料。Kay和Teddy到達烏本橋的時候已是下午16:30,時間不多,所以只有在烏本橋上走一圈,拍些日落的照片。

螢幕快照 2013-11-11 下午5.28.39螢幕快照 2013-11-11 下午5.29.08螢幕快照 2013-11-11 下午5.29.23螢幕快照 2013-11-11 下午5.29.33螢幕快照 2013-11-11 下午5.30.11螢幕快照 2013-11-11 下午5.31.21螢幕快照 2013-11-11 下午5.30.26螢幕快照 2013-11-11 下午5.31.11螢幕快照 2013-11-11 下午5.31.52螢幕快照 2013-11-11 下午5.32.41螢幕快照 2013-11-11 下午5.33.00螢幕快照 2013-11-11 下午5.33.31螢幕快照 2013-11-11 下午5.34.01螢幕快照 2013-11-11 下午5.34.35螢幕快照 2013-11-11 下午5.34.48螢幕快照 2013-11-11 下午5.35.02螢幕快照 2013-11-11 下午5.35.24螢幕快照 2013-11-11 下午5.53.01螢幕快照 2013-11-11 下午5.53.06螢幕快照 2013-11-11 下午5.53.14螢幕快照 2013-11-11 下午5.53.36

 

螢幕快照 2013-11-11 下午5.48.58螢幕快照 2013-11-11 下午5.49.36螢幕快照 2013-11-11 下午5.50.47

 

螢幕快照 2013-11-11 下午5.52.01

螢幕快照 2013-11-11 下午5.52.32

***

友藏內心獨白:走在橋上的感覺還蠻特別的。

2013年11月22日 星期五

Java的try、catch、finally(7):責任分擔

Nov. 14 10:48~13:00

image

 

今天來談一個基本的問題:try、catch、finally這三個block各要負擔怎樣的責任?物件導向設計要解決最基本的問題之一就是責任分派(responsibility assignment),以往責任分派所探討的都在類別(class)與方法(method)的層級上面。要做好例外處理設計,應該要重新審視一下,try、catch、finally block各自應該負擔那些工作。

鄉民甲:這還要問嗎?Java語言不是已經告訴我們,如果程式碼會丟出例外,而且你想處理這些例外,就要把程式碼寫在try block裡面,然後用catch block來捕捉例外並且加以處理。作後,在finally block裡面把使用到的資源給釋放掉。

Teddy:你說的沒錯,這也是一般人所認知的「例外處理」。但請仔細想一下,這樣的解釋是否有助於開發人員真正著手撰寫例外處理程式?就拿catch block所負擔的責任來講:「捕捉例外並且加以處理」。前面這個「捕捉例外」比較沒爭議,但是後面的「加以處理」就有問題了。所謂「加以處理」到底要怎樣處理?

鄉民甲:這個你也不知道喔,很簡單啊,「加以處理」就是呼叫例外物件的 printStackTracr() method把例外給印出來啊。或是把捕捉到的例外轉成另外一種例外型別,然後再往外丟。或是在catch block裡面提供替代方案,取代原本try block的實作。

Teddy:還有嗎?

鄉民甲:大致上就這樣了,還能生出什麼花樣出來?

***

關於這個問題,Teddy以前唸書時在研究例外處理的時候,也是苦思良久。思考的心路歷程就省略,直接說明結論。Teddy認為try、catch、finally各應負擔的責任有:

  • Try
    • Implement requirements (can have alternatives)
    • Prepare state recovery (e.g., make a check point)
  • Catch
    • Perform error and fault handling
    • Report exceptional conditions
    • Control retry flow
  • Finally
    • Release resources and report cleanup failure
    • Drop check points if any

***

Try Block

先來看一下try block的責任,第一點implement requirements(實做需求)這一點很簡單,就是把實做某個功能的正常邏輯寫在try block裡面。所謂的正常邏輯可以有一種以上的實作,也就是說除了預設的實作方式(primary),也可以包含替代方案(alternatives)。

第二點prepare state recovery (為狀態回復做準備)這一點鄉民們可能就比較少遇到。請看下列程式片段,try block裡面的實作會改變物件或是系統的狀態,為了希望例外發生時可以將系統回復到正確的狀態,因此一進入try block立刻產生一個check point(檢查點或是資料備份點),當例外發生的時候,可以在catch block裡面透過這個check point把系統狀態回復到正常狀態。

image

 

其實在try block裡面prepare state recovery,然後在catch block裡面回復錯誤狀態(error handling)也不是什麼新鮮事。有寫過JDBC程式的鄉民應該老早就有這樣的經驗,請參考下列程式範例,con.setAutoCommit(false)相當於告訴connection物件準備一個checkpoint(接下來的操作先不要直接commit),在catch block裡面,con.rollback()這一行就是用來回復錯誤狀態。

螢幕快照 2013-11-14 上午11.53.49

程式來源在此

 

Catch Block

Catch block有三個責任,第一點perform error and fault handling(執行錯誤與缺陷處理)。Error handling和fault handling要分開談,error handling的目的在於如果系統因為例外發生而處於一種錯誤狀態,則需要修正這種錯誤狀態,讓系統回復到可繼續運行的正確狀態。Error handling的例子在解釋try block的時候已經提到了,請參考之前的說明。

Fault handling則是要嘗試排除造成例外產生的根本原因,因為如果光是修正系統狀態而沒有排除造成錯誤的原因,那麼系統繼續執行下去還是很有可能重複發生相同的錯誤。由程式自動來做fault handling是屬於比較困難的工作,因為要事先預知發生fault的原因,才有可能在設計階段把排除fault的方法設計到程式裡面。所以,實務上很多fault handling是交給聰明的「人類」來處理。例如,假設鄉民們正在使用某個文書處理軟體,在儲存檔案時因為同時間有另外一隻程式也開啟了相同的檔案而儲存失敗。這時候文書處理軟體會提示使用者:「該檔案已被其他軟體使用中,請先關閉其他程式。」接著使用者可以選擇「重試」或「取消」存檔的動作。文書處理軟體把fault handling(「請先關閉其他程式」)的責任交給使用者來執行。

Catch block的第二個責任是report exceptional conditions(回報錯誤狀況),類似的例子之前已經出現過很多次了,請看下列程式片段,第107行 throw new InvalidPacketException(“Data Underflow”)就是負責用來report exceptional conditions。

Image (6)

 

Catch block的最後一個責任就是control retry flow(控制重試流程),很多人把catch block當作try block的「備胎」,如果try block執行失敗,則可以在catch block裡面提供一個alternative(替代方案),這也是一般人常見的例外處理作法,請參考下列程式片段。

螢幕快照 2013-11-14 下午12.29.26

但是剛剛Teddy在介紹try block的責任時有提到,提供預設實作方式(primary)與替代方案(alternatives)的責任都屬於try block,所以不應在catch block提供替代方案。上面這種程式寫法Teddy認為是一種例外處理壞味道(bad smell),稱之為spare handler。關於spare handler的詳細討論請參考<敏捷式例外處理設計 (5):我到底哪裡做錯之 spare handler>。

為了讓例外發生之後try block可以有機會執行替代方案,就必須要在Java裡面模擬出retry。下列程式片段就是模擬retry的做法,鄉民們可以先不用管Retry class在什麼麼,總之在catch block裡面的retry.rescue() method(第13行程式碼)就是用來扮演control retry flow的責任。

螢幕快照 2013-11-14 下午12.41.04

 

Finally Block

Finally block有兩個責任,第一是release resources and report cleanup failure(釋放資源並且回報釋放資源失敗的狀況),關於這一點前幾集的內容已經討論很多了,在此就不重複。直接說明第二點:drop check points if any(清除check point)。Try block的第二點責任是prepare state recovery,因此可能會在try block裡面產生一個check point。如果發生例外,這個check point會在catch block裡面用來恢復狀態,用完之後並且會自動被丟棄。但是如果沒有例外發生,則必須在finally block裡面把check point丟掉,否則累積太多check point也會發生系統資源不足的問題。

請參考下列程式片段,finally block裡面的 cp.drop() 就是用來丟棄check point的程式碼。

image

***

以上try、catch、finally的責任,是Teddy「遍覽群書」之後整理出來的看法,鄉民們可以參考一下。Teddy自已的經驗是,有了這樣的責任分派觀念之後,在做裡外處理設計與程式撰寫時,會清楚很多,也比較不容易寫出混亂、易錯、不易看懂的程式。

***

友藏內心獨白:這一系列終於快到尾聲了。

2013年11月21日 星期四

泰迪軟體2014年課表

Nov. 20 21:16~23:00

螢幕快照 2013-11-20 下午11.32.26

 

今年度的公開課程算是告一段落,原本排定的課程還剩下一門「第二梯次Design Patterns這樣學就會了:進階實作班」,不過報名人數還不到最低開課人數,除非這幾天還有學員報名否則應該會延到明年開課。

這兩天把2014年的課程與開課日期整理了一下,目前預計明年至少會新增一門三天的「軟體重構入門實作班」,以Martin Fowler的書和Teddy自製講義為教材,介紹書中提到的bad smells以及移除這些bas smells的refactoring方法。軟體重構是寫出clean code的基本功夫,也是TDD中不可或缺的一個動作。從去年就有鄉民詢問Teddy何時會開這門課,隔了一年多終於有了答案,時間就在:明年四月不要告訴別人

另外,「例外處理設計與重構」這門課會由一天延長為兩天(想講的東西太多不要告訴別人),上過一天課程的學員也不用擔心吃虧,可以用半價來上新的兩日課程。

明年度還有一些其他計畫,時間允許的話會辦有關敏捷需求開發工作坊,也有計畫要開Kanban與精實開發的課程,不過因為很想在明年度把《例外處理設計與重構的逆襲》《設計模式的逆襲》這兩本書給寫完,所以開發新課程的速度會慢一些。

以下是明年的課程表(暫定,但應該不會有太大幅度的調整),請參考。以下所有課程也提供企業包班服務,歡迎有需要的企業多多利用熱戀


[課程]Scrum敏捷方法實作班 (地點:台北市)

  • 假日班:01月11、12(六、日)。
  • 假日班:05月03、04(六、日)。
  • 假日班:07月26、27(六、日)。
  • 假日班:11月15、16(六、日)。

[課程]Design Patterns這樣學就會了--入門實作班 (地點:台北市) 

  • 假日班:03月08、09、15(六、日、六)。
  • 假日班:06月21、22、28(六、日、六)。
  • 假日班:10月18、19、25(六、日、六)。

    [課程]Design Patterns這樣學就會了--進階實作班 (地點:台北市)

    • 假日班:03月22、23、29(六、日、六)。
    • 假日班:11月01、02、08(六、日、六)。

        [課程]例外處理設計與重構實作班 (地點:台北市)

        • 假日班:02月22、23(六、日)。
        • 假日班:05月10、11(六、日)。
        • 平日班:07月08、09(二、三)。
        • 假日班:09月13、14(六、日)。
        • 假日班:12月13、14(六、日)。

        [課程]軟體重構入門實作班 (地點:台北市)

        • 假日班:04月19、20、26(六、日、六)。
        • 假日班:08月09、10、16(六、日、六)。

        [課程]單元測試與持續整合實作班 (地點:台北市)

        • 假日班:05月24、25(六、日)。
        • 假日班:09月27、28(六、日)。

          ***

        友藏內心獨白:設計教材也是很花時間的。

        2013年11月20日 星期三

        Java的try、catch、finally(6):自己製作Suppressed Exception

        Nov. 08 10:47~14:20

        image

         

        複  習

        這系列寫了好幾集,內容又都是程式碼,還沒轉台的鄉民們應該也看到頭暈了。先整理一下之前提談過的一些Java SE 7的try statement關於清除資源的特性:

        1. 用try-with-resources來使用資源物件,在離開try statement之前,JVM會自動幫忙關閉這些資源。
        2. 如果try block或catch block丟出例外,且JVM在關閉資源時也產生例外,JVM會把這些cleanup例外加到之前產生的例外之中,稱之為suppressed exception。
        3. Try-with-resources的功能雖然解決了cleanup例外蓋掉try block或catch block所產生例外地這個問題,但是最終所丟出去的例外,到底是代表function failure或cleanup failure,其語意還是模糊不清。
        4. 藉由讓AutoCloseable介面的close() method丟出CleanupException這個使用者自訂的unchecked exception,可以讓明確區分try-with-resources的function failure與cleanup failure語意。但是,可惜Java內建的資源物件,其close() method並不會丟出CleanupException,所以在預設情況下還是語意不清。
        5. 第2點所提到的功能,只有在try-with-resources的情況下JVM會幫忙產生suppressed exception,如果是傳統的寫法,finally block所產生的例外,在Java SE 7 之後的行為依然沒變,還是會把try block或是catch block所產生的例外給「蓋台」。

        複習完畢,今天的目的想要解決第4點與第5點所提到的問題。首先寫段程式確定一下第5點所描述的現象。請看以下Java SE 7的程式碼片段,在finally block中直接丟出IOException(第35行)。

        螢幕快照 2013-11-08 上午11.17.35

         

        在main()程式測試一下finallyBlockOverrideExceptionThrownByTryBlock() method。

        螢幕快照 2013-11-08 上午11.19.21

         

        結果發現,沒有任何suppressed exception,捕捉到的例外是由finally block所丟出來的IOException,原本try block所丟出的IOException已經被蓋台了。Java SE 7的finally block的行為模式和之前版本並無不同。

        螢幕快照 2013-11-08 上午11.24.21

        ***

        老師傅手工打造

        接下來將會使用到MyConnection、MyInputStream、MyOutputStream這三個類別,由於要模擬Java內建的資源物件,因此這三個類別的close() method不再丟出Teddy自訂的CleanupException,而是丟出Java內建的SQLException、FileNotFoundException、IOException。

        螢幕快照 2013-11-08 下午1.09.27

        螢幕快照 2013-11-08 下午1.09.44

        螢幕快照 2013-11-08 下午1.10.10

         

        接下來先看function failure的例子,之前已經說明過try-with-resources無法清楚的區別function failure與cleanup failure兩者的語意,因此只好用傳統的finally block加上一些程式設計技巧來嘗試解決這個問題。請先看一下下圖useCleanerWithFailureException() method的程式碼。

        螢幕快照 2013-11-08 下午1.13.47

         

        整個設計與使用概念其實很簡單,只有三個步驟:

        1. Teddy設計了一個Cleaner類別,利用這個類別來模擬JVM把資源物件push到一個stack的動作(24~26行)。
        2. 由於try block與catch block都可能會丟出例外(在此稱之為lead exception),為了有機會可以把cleanup exception加入到lead exception裡面,因此在28~31行用一個blanket catch捕捉住除了Error以外的全部的例外。然後,把呼叫Cleaner類別的setLeadException() 方法,把捕捉到的lead exception先存起來。等一下如果在finally block發生cleanup例外,才有辦法把cleanup例外加到lead exception裡面。
        3. 最後,在finally block呼叫Cleaner類別的clear() method,執行資源釋放的動作。

        接下來將會使用到MyConnection

        接下來看一下執行結果,的確是捕捉到了代表function failure的IOException,而三個型態屬於CleanupException的suppressed exception也都被正確加入。

        螢幕快照 2013-11-08 下午1.43.30

        ***

        Function failure的語意正確了,接下來看一下cleanup failure的語意是否正確,先看範例程式,和剛剛function failre的例子差不多,只是現在try block不會丟出例外。

        螢幕快照 2013-11-08 下午1.46.58

         

        執行結果,的確是捕捉到了代表cleanup failure的CleanupException,而且suppressed exception現在變成只剩二個,也都被正確加入。

        螢幕快照 2013-11-08 下午1.48.50

        ***

        最後看一下Cleaner程式碼,運作原理剛剛已經說明過了,程式也很簡單,不再贅述。

        螢幕快照 2013-11-08 下午1.53.25

        螢幕快照 2013-11-08 下午1.54.05

        ***

        例子看到這邊,鄉民們可能會覺得:有必要為了區分function failure與cleanup failure把程式寫成這個樣子嗎?直接用try-with-resources不是簡單又方便嗎?這個問題其實Teddy也沒有什麼「正確答案」。之前念書的時候有一陣子在研究例外處理,發現例外處理很複雜,其中原因很多,而程式語言沒有清楚的區分function failure與cleanup failure就是其中的原因之一。

        請回想一下自己寫過的Java或C#程式,請問大家都怎麼處理close()所丟出的例外?大部分的人不是忽略它,就是反射性的印出例外訊息然後這個訊息就被掩沒在其他更多的訊息之中。到底呼叫close()發生例外的機率高不高?依據Teddy的經驗,不高。但是一旦發生卻又被有意無意忽略,則這樣的問題是很難被找出來的。

        好幾年前Teddy曾經用過一個第三方廠商所寫的免費JDBC驅動程式,用來連接某個資料庫。在那個年代,有些JDBC驅動程式是需要付費的,所以Teddy找了這個免費的驅動程式來使用。剛開始用得時候都很正常,一直到上線之後,發現程式跑了一陣子就會把資料庫的connection數目全部使用完畢,造成後續的連線要求全部失敗,導致需要重新啟動資料庫。

        一開始Teddy以為程式中釋放資源的程式碼沒有寫好,後來搞了好久,才發現呼叫connection物件的close() method丟出了例外,一直都沒被注意到。看起來是這個免費的JDBC驅動程式有bug,最後只好換另一個JDBC驅動程式才解決了這個問題。

        可能是因為有這個經驗,所以Teddy對於cleanup failure才有自己的一點小小堅持吧挑眉質疑

        ***

        友藏內心獨白:程式要不要這樣寫不是重點,解題思考模式可以參考一下。