2015年1月31日 星期六

程式的強健度等級

2015/1/31 20:23-21:22

今天來講一下程式的強健度等級,強健等級分為四級,分別為等級零、等級一、等級二、等級三,共四個等級。





等級零 (G0):
沒有任何例外處理,一般工程師在時間緊迫的情況下,只會實作正常流程,毫無考慮及處理異常流程,當異常流程發生時運氣好則程式崩潰,工程師可直接複製情境解決問題,運氣不好不知道怎麼複製情境,也就只能燒香拜拜,祈禱它下次不要再出現。

等級一 (G1):
異常發生就須通報使用者且終止程式。問題發現的越快也就能修正的越快。

等級二 (G2):
假設現在有個函式無法達成所規範的規格,就必須把狀態回復到還沒執行這個函式的正常狀態。

等級三 (G3):
是個使命必達的FU。達成目的的方法有很多路徑,執行一個路徑失敗,就換成另一種路徑試試看;或者一直重試,試到規範的次數,如果還是失敗才不得放棄。



強健度等級是個逐步升級的過程,如果升級不成,就只能降級處理啦!原本強健度等級3的如果無法使命必達,只好採取等級2的策略,將狀態恢復到還沒執行函式的正常狀態,降等級2不成就只好繼續往等級1降,將異常回報給使用者且終止程式。


以上圖片擷取自



更精采的內容請參考Teddy的博士論文<<Exception Handling Refactorings: Directed by Goals and Driven by Bug Fixing>>,如果不想看英文版也沒關係,還有特別整理過的中文版<<例外處理設計的逆襲>>


原來例外處理還有這麼深的學問啊!

2015年1月24日 星期六

一年前的我在旅行-勇氣11班 Day 1

2015/1/24 10:05-11:28


上一年的這個時候,參加我在旅行所舉辦的鐵道大富翁活動,這個活動為期5日,在出發當天會在台北車站集合,總共約70個人,要分成兩大組,抽籤決定將會往東部出發或是往西部出發,再來會抽小組員,每一個組別大概4~5人,每一個小組將會一起遊玩整整五天,每個晚上東部組和西部組都有固定的住宿據點,玩了一天之後,晚上回到住宿的地方,會有個分享晚會,每個小組分享旅行的故事。一個完全沒有任何計畫的旅行就此展開了!!


我抽到了西部組,小組有四個旅客外加一位紀錄組成員。


第一站 鶯歌

對這邊的印象只有一堆陶瓷和土,曾經因為學校的參訪活動,去過陶瓷博物館做陶藝,對於鶯歌完全不熟,從後站走出來走著走著遇到一位阿伯,問阿伯這附近有沒有什麼地方可以去,阿伯和我們一群人說可以去後面的鶯歌石,順便念了我們幾句說:「一群人出來什麼都沒有計畫是要玩什麼?像我出去玩都會先計畫,到了那邊再把東西一次玩遍!」,我有稍稍頂嘴一下,然後就索性不理他。

到鶯歌石需要爬一段小步道,在步道的山腳下,有一間廟


廟本身不怎麼有趣,有趣的是這裡有位慵懶的門神


剛好打擾到門神睡覺的時間,睡得十分熟,怎麼搓牠都沒有反應,和門神打過招呼之後往鶯歌石的方向出發


爬了不短的步道


終於到了鶯歌石


鶯歌石很大一顆,坐落在山頭,這裡也是個眺望台,可以看遍整個鶯歌鎮。

小憩一會後,往另一邊的山腳下山,走著走著,發現古老的控窯住宅



還有個小公園


鶯歌旅程到此結束。

第二站 內壢
火車站坐落在馬路邊,熱鬧區域,馬路上的車流猶如東區,交通阻塞頗嚴重,行人只能避在小小的馬路邊,在這邊吃了中餐便往下一站出發。

第三站 北新竹
火車站附近只有高樓大廈和快速道路,人能走的路都是只有天橋路




第四站 竹中
充滿綠色植物的鄉村,彷彿身在叢林,沒有高樓大廈,只有高大樹仔。






第五站 九讚頭

站長室關門大吉,可以隨意行走月台間的鐵軌,周圍只有叢林和一條山路。因為遲遲等不到火車,所以組員們決定搭便車,對我來說可是一大挑戰啊!搭便車總覺得有一丁底不自在。



組員在攔便車的景象


今日最終站 內灣

便車司機載我們從九讚頭直達內灣,到的時候才下午六點多,老街裡的所有小店都已休息。



晚上七點到達民宿,開始今晚的分享會





2015年1月17日 星期六

練習程式的道場-Coding Dojo

2015/1/17
15:31-15:39, 8 minutes
18:51-19:13, 22 minutes
19:15-19:22, 7 minutes
20:13-20:47, 34 minutes

Dojo從日文翻譯而來,是道場的意思,Coding Dojo的活動,由Dave Thomas最先提出。

Dave Thomas 說:「In software we do our practicing on the job, and that's why we make mistakes on the job. We need to find ways of splitting the practice from the profession. We need practice sessions.」

中文意思:「我們都在工作的時候練習軟體,這就是我們為什麼在工作犯錯的原因,我們需要找到把練習環境和工作環境切開的方法,我們需要一個練習軟體的會議。」

Coding Dojo有一個wiki網站在這裡,裡面有介紹舉辦Coding Dojo須注意的事項,在Coding Dojo之中依活動的流程分成兩種Kata(Kata也是日文,是『形』或『套路』的意思,就像練習功夫的人,有詠春拳或螳螂拳,每一種拳法,都可以算是一種Kata):

***

1. Prepared Kata

準備式套路,指說一個人已經準備好問題和答案,在活動進行中,要讓在場的觀眾體驗解決問題的過程,報告者解題的每個步驟要非常小(Baby Steps),如果觀眾對於正在進行的步驟有疑問,可以馬上中斷台上的報告者,報告者講解清楚後,才能繼續下一個解題步驟。

要注意的是,在活動進行時,觀眾所提出的問題,不能與報告者所進行的步驟無關,舉例來說,觀眾不得要求重構某項功能,這些與功能性無關的要求只有在報告者帶完整個活動後,才可以提出。

2. Rendori Kata

亂鬥式套路,不用做任何的事前準備,只須將所要練習的問題帶來,活動開始的時候,會有一個主駕駛和副駕駛,一次只有兩個人在寫程式(Pair-Programming),每一組限制一段時間(Time-boxing),當限制時間到了,主駕駛回觀眾席,一個觀眾變成副駕駛,台上的副駕駛變成主駕駛,開始計時,時間到了之後,繼續換位,現場全部的觀眾的輪過一次駕駛和副駕駛時,活動才算結束,台上的人也要遵循Babay Steps,把自己的每一步,盡量表達給台下的觀眾知道。
活動過程中,觀眾只有在台上寫程式的人達綠條(Green bar)的時候才能提出問題或提供更好的建議,如果台上寫程式者遇到紅條(Red bar),觀眾就不能提問,因為台上的人必須要專注排除問題。
但因為大家都是臨時Coding,多少都會產生一種競爭的心態,想要在自己寫程式的回合中產生出最多且功能正確的程式碼,或者是時間到了還賴著不走,這時候叫就靠帶活動的人維持秩序啦!

***

其實在帶Coding Dojo活動的時候,大部分的人都容易犯一個錯誤,就是帶活動的人想要讓活動都在預期的時間結束,而在這預期的時間之中,會先事先做好一些準備,在活動開始的時候,希望把所準備的東西全部表達出來,但往往會無法預測台下會提出怎樣的問題。
要切記,Dojo活動志不在呈現結果或完成任務,活動的初衷是希望創造一個練習Coding的環境,大家可以在活動過程中彼此學習到Coding的技巧,寧願活動前進步驟緩慢,也不要因為想把活動帶完而跳步驟,即使當次活動沒有完成任務也沒有關係。

目前在網路上,可以找到許多的Kata題目,只要照著他們的步驟完成程式,就能學到不少東西哦!

這是一個保齡球遊戲,網站有PPT供下載,遊戲很簡單,只須按照PPT描述的步驟就能完成任務,大家可以來玩玩看。



與你分享的快樂勝過獨自擁有→愛唱歌的人都知道的那些事

2015年1月10日 星期六

Java中的Checked Exception與Unchecked Exception

2015/1/10 13:31-15:19

在這個星期,終於完完整整地將Teddy的<例外處理設計的逆襲>讀完一遍,在最後面有一個附錄叫做視力測驗,這個章節是測試自己對於書中介紹的還記得多少,全部共20題,答對其中14題以上才算及格,如果低於14題,Teddy建議讀者要再閱讀幾遍,作答過程中自己都很有把握,最後竟然才剛好對14題,差一點點就要不及格,突然回想到自己國中國小考選擇題的時候,常常把題目『下列何者錯誤』的題型看成『下列何整正確』,不是不會只是題目不小心看錯啦XD(給自己的小小藉口)。

這次稍微介紹一下Unchecked Exception和Checked Exception

在Java程式語言的例外處理中,依編譯器檢查程式碼的的觀點,分為兩類
1. 受檢例外 (Checked Exception)
2. 非受檢例外 (Unchecked Exception)


Checked Exception代表在程式編譯的期間,編譯器如果發現開發者沒有處理Checked Exception的話,它會把這個情況視為錯誤,強制開發者修正後,才能成功編譯程式。

相反地,如果例外是屬於Unchecked Excpetion,使用者不須多加處理,程式就可以通過編譯器的檢查。

在Java程式語言中,所有的例外都繼承自Throwable類別,繼承關係如下圖:




在上圖中,Throwable、Exception、IOException和SQLException屬於Checked Exception;
Error、RuntimeException、IndexOutOfException和NullPointerException屬於Unchecked Exception。



現在舉例讓各位體驗一下在撰寫Java時,遇到Unchecked Exception和Checked Exception的情況,我使用的IDE為Eclipse。

圖1 編譯器提示錯誤

圖1中,我在主程式main中呼叫了testCheckedException,它會丟出IOException,IOException屬於Checked Exception,編譯器在第11行出現一個錯誤,提示我必須在main的介面上宣告捕捉IOException,才能通過編譯器的檢查。

圖2 將例外宣告至函數介面上

圖2將IOException宣告在介面上的程式碼,此程式通過編譯器的檢查,這個例外最後會由捕捉它的人處理,也就是呼叫main的函數處理,或者呼叫main的函數可以繼續往外丟。在這個例子裡,誰會呼叫main呢?答案就是處理Java Process的Java Virtual Machine(JVM),JVM捕捉到例外之後,會將例外印在Console視窗接著把程式中止。

執行程式的結果如圖3


圖3 JVM收到例外


現在將圖1的程式,改為捕捉IOException,使用Eclipse自動修正後程式碼如圖4。


圖4 捕捉IOException且印出該例外的詳細資訊


圖4中的第14行程式碼會印出例外的詳細訊息,結果如圖5。


圖5 執行e.printStackTrace()在Console會印出例外的詳細訊息



以前剛寫Java的時候,以為程式只要發生例外,就算有捕捉(catch),程式跑完catch區塊的程式碼還是會中止,但事實不是這樣的,執行完catch區塊的程式碼後,程式還是會繼續執行下去,看以下的例子。

把圖4中的程式碼稍作修改,執行並觀察其輸出狀況如圖7。

圖6

圖7


由圖7的輸出結果可以觀察出程式執行的狀況,當程式執行到第13行時,發生IOException,程式碼直接跳到第16行執行catch區塊的程式碼,接著繼續往下執行到程式碼第20行。



接著我們繼續測試Unchecked Excpetion。


圖8

在圖8中,main呼叫了testUncheckedException(),此函數會丟出RuntimeException,它屬於Unchecked Exception,此程式直接通過編譯器的檢查。

當程式執行到第9行,main丟出例外給JVM,JVM會將例外資訊印出且中止程式。執行結果如下圖9。



圖9



以上就是今天的介紹,下回見。


我的例外處理層次好像多了一層


2015年1月3日 星期六

玩一玩Android外部記憶體

2015/1/2 14:41-15:15, 22:32-24:04

年假在家裡休息無所事事,昨天花一個下午的時間稍微試一下Android的外部記憶體,也就是在裝置中的/sdcard/目錄的使用方式。

在Android 4.4之前,只要在AndroidManifest.xml中加入android.permission.WRITE_EXTERNAL_STORAGE,就可以自由地使用外部路徑,使用API Environment.getExternalStorageDirectory()可以取得外部記憶體的路徑。約在一年前,Android作業系統大部分都使用在手機上,那時的HTC最高階的手機是HTC Sensation,同一時期的各家廠牌手機,外部記憶體通常指的就是Micro SD卡,開發App時,將檔案寫在外部記憶體很方便,能透過ADB或USB隨身碟的方式將檔案拉到PC上。

一直到了HTC機皇是HTC One X的時期,原本是外部記憶體路徑的/sdcard/,變成虛擬外部記憶體,也就是說,這個/sdcard/目錄,並不是實體Micro SD卡的路徑;而是手機內部空間虛擬成的外部記憶體,這個虛擬而成的外部記憶體一樣須加上permission後才能正常讀寫檔案,在開發階段很方便,不需要真的取準備一張實體的Micro SD卡,就可以自由地將檔案從手機拉到電腦上。

但也衍生一個問題,如果真的想把檔案存到實體的Micro SD卡中,該怎麼辦呢?目前Android也沒有提供相關的API取得實體外部路徑,但在網路上有一些非官方的方法可以取得真正SD卡路徑的,但不是每隻手機都適用。

在Android 4.4剛出來的時候,很興奮的把我的Asus Nexus 7升級,升級之後原本的程式一直當機,追查後發現是Android 4.4以後的版本,sdcard有做一層保護,App不能直接使用外部記憶體(不管是虛擬或實體外部記憶體),需透過手機的設定,才能把整隻App移到sdcard上,但還是有一個缺點,把App移動sdcard上後,只要移除該App,所有和該App關聯的檔案(包含使用該App所產生出來的所有檔案)全部都會被刪除。天殺的Google這樣改,超級麻煩,在開發階段的App,動不動就須移除程式重新安裝,產生好的假資料每安裝一次就要再建立一次,怕麻煩的我決定重回Android 4.2的懷抱,把Nexus 7先丟一旁,拿實驗室的其他手機來開發。

最近使用ES File Browser (最初它也不能再Android 4.4上正常運作),突然發現它在我的Nexus 7上竟然可以正常存取外部記憶體中的檔案,於是我好奇地重回Android 4.4的懷抱,參考這裡寫了一隻在外部記憶體上寫檔案和讀檔案的程式,檔案竟然可以操作了!操作方法跟以前一模一樣。

在Android 4.4多了一個permission:android.permission.READ_EXTERNAL_STORAGE,顧名思義就是讀檔案的權限。
寫檔案的權限已經包含讀檔案的權限,也就是說如果加入android.permission.WRITE_EXTERNAL_STORAGE,就可以不用再加入android.permission.READ_EXTERNAL_STORAGE,Google也很用心地提供了isExternalStorageWritable()isExternalStorageReadable()兩個方法,用來判斷外部記憶體是否能讀寫。

於是我做了小實驗,觀察isExternalStorageWritable()isExternalStorageReadable()的回傳值:

在沒有加入任何權限的情況下,
isWritable()回傳true
isReadable()回傳true
但當我執行檔案讀取或檔案寫入都會失敗,
蝦咪!?明明不能讀寫檔案還給我回傳true。

追了一下程式發現這兩個方法都是藉由Environment.getExternalStorageState()的回傳字串判斷外部記憶體的狀態是否可讀可寫,於是我又在HTC Butterfly測試,HTC Butterfly可以插實體外部記憶體(Micro SD卡),觀察實體外部記憶體在插上或拔除的時候Environment.getExternalStorageState()會不會取得其他不同的狀態,結果當然沒有這麼順利,它竟然永遠都是回傳Environment.MEDIA_MOUNTED
PS: HTC Butterfly的"/sdcard/"目錄是屬於虛擬外部記憶體而非實體外部記憶體。

接著我決定實作看看『正確地判斷/sdcard/可讀或可寫』的方法,花了約六個小時的時間(因為嚐鮮透過Android Studio加上最近剛學的TDD實作),這邊直接給大家我完成的程式碼。

先定義一下我所個人認為的isExternalStorageReadable()isExternalStorageWritable(),兩個方法的理想行為

isExternalStorageReadable():

在沒有加入android.permission.READ_EXTERNAL_STORAGEandroid.permission.WRITE_EXTERNAL_STORAGE時,回傳false
反之則回傳true,代表可以讀取在外部記憶體的檔案。

isExternalStorageWritable():

在沒有加入android.permission.WRITE_EXTERNAL_STORAGE時,回傳false
反之則回傳true,代表可以寫資料在外部記憶體內。




實作內容如下(黃色部分):

----------------------------------------------------------------------------------------------------
boolean isExternalStorageReadable() {
        return Environment.getExternalStorageDirectory().canRead();
}

boolean isExternalStorageWritable() {
        boolean writable;
        final String filePath = Environment.getExternalStorageDirectory() + 
                                       "/" + "testExternalStorageWritableFile";
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(filePath);
            writable = true;
            // delete test file
            File file = new File(filePath);
            file.delete();
        } catch (FileNotFoundException e) {
            writable = false;
        } finally {
            if(fos != null)
                try {
                    fos.close();
                } catch(IOException e) {
                    e.printStackTrace();
                }
        }
        return writable;
}
----------------------------------------------------------------------------------------------------


最初的isExternalStorageWritable()使用Environment.getExternalStorageDirectory().canWrite();
但我加入android.permission.READ_EXTERNAL_STORAGE後它就回傳true了,在這個情境下要回傳false才對。

所以最後用了比較麻煩的方法:

如果沒有加入android.permission.WRITE_EXTERNAL_STORAGE,
執行new FileOutputStream(...)就會捕捉到FileNotFoundException,此時代表外部記憶體不可寫。

加入android.permission.WRITE_EXTERNAL_STORAGEnew FileOutputStream(...)不會發生例外且產生檔案,代表外部記憶體可寫,但最後必須將產生的檔案刪除。


以上是我小小的分享,祝各位新年快樂!



有疑問就去挖(一直"那些事"好煩喔,暫時讓它消失,有需要再讓它出現)