2013年10月19日 星期六

專業主義的秘密 - 練習


先前和朋友聊到,最近同時間看一套日劇 Dinner 和 著名的 The Clean Coder  一書,被其中的專業主義感動到不行。

The Clean Coder  剛看完第六章,章名是我從來沒有跟大家分享過,卻是我奉行已久的關鍵字 - 練習。

我絲毫不想透漏Dinner的劇情,以及Robert C. Martin在The Clean Coder ㄧ書中提到關於練習的描述。這些東西(看Dinner這部片以及The Clean Coder這本書)將是屬於你這個月最頂級的享受,我不忍心剝奪它。但我忍不住要說,Dinner男主角在每天下班後忙到深夜的事情、和Robert C. Martin用了一整章說的故事,說到底,都只有簡簡單單卻始終被大家忽略的一個字,練習。

這年頭,很少程式設計師會進行"練習"了,而且是重複的練習。但你絕對沒有意識到,這件是有多麼的重要...老實說,如果不是因為要上課,我自己大概也沒有機會有這麼深的體會。但是,在這邊我要跟大家分享的,就是這近十年來,我擔任教育訓練人員、作者、或是顧問時,一直奉行不渝的 -- 練習。

練習什麽呢? Robert在書裏面有說,像是kata, wasa之類的(這你要看書才知道,說過了,我不想剝奪你的樂趣)。但我想跟大家說我自己的經驗,那就是『研討會前的練習』。

如果你熟悉我,你就會知道我講話的速度應該不算慢,在研討會上,我盡可能在兼顧清楚的情況下,以最快的速度跟大家分享,同時間,每一場次的研討會,我都要求自己,要盡可能地與學員互動。

我們也都知道,大夥兒希望看到Live Demo,也因此,我的每一場研討會,幾乎都會有Live Demo的部分,拿今年TechDay Taiwan 2013的例子來說,架構設計的場次中,我簡單Demo了應用程式的分層開發、Unity Application Block的例子、Logging Application Block的例子。在WP8的場次中,我介紹了動態磚、Lock Screen、NFC、App Communication,其中都不乏Live Coding。

一場研討會的時間是70分鐘,其實講師們都清楚的知道,只要Live Demo當中,有一個環節不正確或出現意外狀況,幾乎就有可能讓整個Session毀掉,你不可能讓台下的學員看你在上面Debug、Try and Error...再Debug,或重開機...。也因此,Live Demo要盡可能的行雲流水,如果是超過兩三個範例,那麼挑戰就會倍增,如果再加上幾個不確定性因素,例如網路連線狀況(速度慢或斷線)、投影機(螢幕解析度)、緊張(不熟悉的場子)、麥克風(沒有領夾式的麥克風,導致只剩一隻手敲鍵盤)...這些都會大大影響整個Live demo的狀況,也因此,要順利成功的進行Live Demo,關鍵因素只有一個,就是大量的練習。

你要不要猜猜看我在每一場大型研討會前,要Live Demo的內容有做過多少次練習? 我要告訴你,每一個範例、每一個動作,絕對都"完全一樣地"重複練習超過三次以上,如果時間夠,我會練習到五次,其中一次會錄影起來,以備不時之需。

同樣Demo一個範例,例如用WP8建立一個iconic tile,我會在有網路、沒有網路的狀況下各練習一次,接著把螢幕切成只有1024x768的時候,再練習一次(因為投影機大多只支援到這個解析度)。練習時寫的Code完全不變,就是換不同的環境,如果時間允許,我會換一台NB(或開一個虛擬機),再練一次。

因為多年的經驗,我們清楚的知道,有網路和沒網路很可能Demo效果和路徑(操作步驟)會完全不同,在1024x768或1280x800的解析度下,滑鼠點選螢幕上的某個視窗,位置可能會不同,這會影響Live Demo的路徑、而路徑會影響速度,甚至會影響你的信心。

因為我們清楚的知道,一旦因為解析度不同、或是網路沒有連線,導致正式上場時Live Demo的路徑(操作步驟)和在家裡操作的不同,講師很容易緊張,ㄧ緊張,就會出錯。

所以我得說,你在現場看到ㄧ次順利的Live Demo,其實往往是講師(至少是我),以完全同樣的步驟在家裡、公司,練習過了三五次之後的結果。先不談準備內容和撰寫範例,光花費在重複練習的時間,已經超過研討會正式上場時間的五倍以上。

但是充分的練習,才能讓我們正式上場的時候不緊張,才能夠在各種環節與情境下,都隨時保持著自信、泰然自若的面對各種可能發生的問題。Live Demo時,打錯一行字,出現ㄧ個Bug,不急,別怕,因為我練習的時候碰到過,不擔心,我知道怎麼解決。

我告訴你我在練習時碰過的幾個極端的例子,包含建立專案的時候不能Build,是因為專案資料夾名名稱+檔案名稱超過Visual Studio的長度限制;用NuGet安裝套件,碰到臨時沒網路忘了要裝那些.dll;開的專案範本不同,套件在.net 4.5才能run,Demo時候卻開到.net 4.0的範本,某些.dll裡面隱含著用了另一個不知名的.dll卻忘了add reference...這種狀況,如果是在台上臨時遇到,我可能直接束手投降,放棄這一段的Demo。但沒有,你幾乎沒看到我們在台上這樣,因為那些問題正式上場時幾乎沒發生過。而沒發生,是因為我知道可能會有這個狀況而先避開了,這些...都是因為事前的練習。

最近這幾年,我習慣把練習的過程順便錄成影片,因為要錄影,所以會有我的旁白,也就是錄影時候的說明,這個說明,事實上又是另一次的練習。我除了在上台前會將Live Demo練習幾次,包含每一張投影片上該講的內容,都會試著整個走過一遍,如此一來就可以知道大概需要多少時間,哪一張投影片需要長一點、哪一張投影片可以稍微帶過即可。而練習時Live Demo的錄影,也可以讓我預先練習現場要跟學員怎麼說明和介紹這段程式碼,因此我幾乎在比較大型的研討會,都會事先把影片錄好的原因,錄影的過程,也是一種練習。

如此一來,準備一場研討會,我大概寫了5-10次的動態磚建立、5-10次的Unity Application Block的範例、5-10次的Logging Application Block的範例5-10次的Lock Screen、5-10次的NFC、5-10次的App Communication...

到最後,一場研討會下來,最熟悉整個開發的流程,收穫最多的,其實就是我自己。上課強迫我非得hands-on,而實際下手去做,才是真正"有效的學習"。
===================
後記:
你可能覺得,我又不是講師不多機會上台介紹技術,那還要練習幹嘛? 其實,只要你是ㄧ個程式設計師(甚至是Sale, Presale)、需要跟客戶、老闆、長官 Demo、需要準備驗收的UAT,需要對內部同仁進行技術分享,你的Demo都應該要事前練習,甚至只是看ㄧ本書之後的心得記錄,寫寫blog...這些都是你可以練習的機會,有機會,就不要放過,前面說過,多練習,實際下手去做,才是真正有效的學習。

2013年10月17日 星期四

如果人人都能寫程式???



今天早上看到這一篇文章:
http://share.inside.com.tw/posts/2775
還有以前的一兩篇文章
http://mrjamie.cc/2013/10/17/programming-generation/

讓我忍不住想講一些自己的觀察(感受),當然,從很久以前我就知道我其實是屬於少數人那一掛的,也就是說,當網友們極力反對或贊成某些事情時,我常常不知好歹的站在另一邊。最近我也常常FB一些其他人不太同意的論調...不過慶幸我生在一個還算和諧的時代,我不會因為這樣就被燒死...(因此請大家也別太認真,我的論點絕對有可能是錯的,甚至很多時候,我也曾故意寫過一些些不是那麼精準和正確的內容...)

早上看到的那篇文章,讓我直覺地想到,未來幾年我們很可能會開始活在一個人人都能寫Code的時代。說人人有點誇張,但比例勢必會大幅提升,原因很簡單,你有沒有發現,現在大家都在教別人寫程式??? 而且只要你願意,幾乎可以不用任何成本的學會寫一套程式(大概只需要花點錢買NB、和上網)

最近這十年,Coding這件事情有幾個重大的改變:
  1. 開發人員年齡層與進入門檻持續下降(年輕駭客、或是年輕億萬富翁比比皆是)
  2. 軟體開發資源突然間變多(拜internet普及、全球化資訊自由流通之賜)
  3. 通用軟體價值(實際售價)開始變低(這有個歷程,從30年前硬體收費軟體免費,變成20年前軟體很貴硬體便宜賣,到最近10年又有點變成軟體免費或隨著硬體銷售的趨勢)
  4. 軟體以服務的面貌收費

特別是第四點,很多人覺得似乎沒什麽,ASP或是SaaS都講了很久了,但這十年的改變異常之大,讓人措手不及。

以前獨立開發人員,可以在車庫裡面寫出一個mail收發軟體,然後放上BBS網路銷售,或是讓網友donate這個軟體。但這幾年這樣的模式還是有,但越來越限於特定族群、特定領域的應用程式。才不過五年前,大家搶著寫App賺錢,現在卻又說App不用IAP就賺不了錢!!! 終端消費者(End-User)開始視軟體為免費是理所當然的事情。

過去三十年來所建立的軟體有價的概念,隨著internet on line services普及而開始崩解,現在的終端消費者,越來越覺得軟體應該不能收錢,如果要跟我收錢,那最好你能提供像Google一樣的服務再說,否則程式設計師想收錢? 門都沒有,我用google app就好囉。

軟體開發人員必須知道,軟體免費的時代快來了,如果市場能接受軟體免費而服務收費,其實對於大型企業來說無形中建構出了一道擋住中小企業的進入障礙與門檻。

Google的"服務"有效的(在暗地裡逐漸)改變了軟體消費者的使用習慣以及價值觀。Google甚至改變(加速)了微軟的軟體銷售模式(Ex. Office 365)。

然後,軟體世界就變了。

一直以來,我並不是一個強烈擁護應該將所有資訊與開發技術、技巧100%都放到blog上公開的人。那些標榜著分享、Open...等等的偉大主張,我並不是完全同意與支持。理由很簡單,我壓根不覺得分享是理所當然的事,如果你長時間的分享,你就會發現,除非你覺得分享這件事情本身是個極大的樂趣,否則它真的是一件很辛苦的事情。除此之外,分享的成本很高(而且當你夠老之後,就會發現這個成本還會隨著年紀越來越高),因此沒事不會有人寫個幾百篇blog只是為了一個崇高的理想。

我得說,其實掌握這些知識(資源)的人很多很多,很多人躲在企業裡面,能力不輸講師、作者,只是下班後還希望有更多的家庭生活和自己的時間,因此壓根不想浮上檯面。說真的,其實沒有人生來就應該要分享。blog作者所分享的東西,常常是自己花了十多倍的精神和體力,才得到的一點點經驗,說穿了其實也就不值錢了。但卻能省去其他人花費相同的時間。知識的分享本身真的是很崇高的。

回想,年輕時候的分享真的只是分享,動機很單純,只是因為發現了一些什麽,忍不住想要告訴更多人。但為什麽分享這件事情,年紀越大就越少? 職位越高就越少? 因為隨著年紀,人生的挑戰開始變多,許多人自顧不暇,更罔論持續的分享了...

(有時候分享還會被指指點點,就更令人氣結:再有時候分享完全沒有任何人指指點點,那就更是一種淒涼了...)

所以你看到的分享,要麼就是作者的偉大情操(這種你要感謝),要麼就是背後多多少少有著一些原因或目的,特別是技術從業工作人員的分享(這種你要保持警醒)。

最近十年,整個開發社群、Open Source社群、服務導向的公司,將"軟體開發技術分享"這件事情,形塑成一個很崇高的理想、或是開發人員的使命。有點那種,我share故我在的終極使命感。

但我想換個角度提醒大家,不知道你有沒有發現,分享的單位(或個人)所分享的內容,往往並非自己的核心競爭力,或是核心競爭力的來源。我不方便描述得很明確,我只能說,只有在分享對分享者(個人或企業)本身是無害(甚至有利)的時候,分享這件事情才能長久持續。

所以你慢慢發現,網路上基礎程式設計的分享變多了,開發技巧的分享變多了,特別是在北美成熟穩定的開發人員世界,由於開發人員的普遍薪資比較高(比台灣),所以基礎開發技術的分享俯拾皆是。但這對於活在像台灣這樣的環境的資訊軟體從業人員來說,是好還是不好,是有利還是不利?

比起個人,大公司或大企業,更會清楚的盤算利弊得失,我相信,對於大企業來說,鼓勵開發人員"針對開發技術"來分享,最終絕對是利多於弊。

我挑明了說一句,當基礎開發技術俯拾皆是、唾手可得的時候,事實上最有利的很可能是業主、老闆,因為開發人員的薪資沒有(無法)變高,但開發成本明顯降低了。這對推廣技術(把餅做大)、降低成本絕對是有利的,但這對於基礎開發人員來說,則不見得是全然有利的(所以回頭想想,開發人員不顧自己生計地持續的在分享,這才真真正正的是一個偉大的情操)

當然,這對整體社會來說好不好? 我認為好,因為更多的人受開發相關的教育訓練,對於邏輯和分析能力有相當大的幫助,對整體社會來說我想會是好事。犧牲一部分開發人員的權利,來換取整體社會的進步好不好? 我認為好!(你可以想,因為自私的David不是基礎開發人員,所以根本沒差...恩...是這樣嗎?) 但正在看這篇文章的你呢? 我很想知道你覺得好不好?

總的來說,大量的軟體開發技術分享,將會壓抑軟體開發成本。不過我們本來就在追求更低的軟體開發成本,不是嗎? 每一個開發人員不是應該時時求進步,時時處於學習與戰鬥狀態嗎? 如果你是這麼期許自己的,那真的值得恭喜與驕傲。的確應該如此,這是軟體開發人員的使命。我自己也是這樣期許自己...

軟體開發人員要有志氣(與勇氣)的對自己說:『我們是走在時代前端的開發人員,不是傳統產業,我不擔心工作被搶,我們不怕國際化、全球化的挑戰!!!』

但,如果你想知道為何軟體開發人員薪資似乎越來越低,那我也得誠實的告訴你,在現在這種開發技術隨手可得的時代,如果沒有每天學習,你的薪資就真的是只可能會越來越低。你說有些行業的技術幾十年不變、不需要每天學習,像是會計師、律師就不用啊,不公平!!! 對,答對了,看起來就是這麼的不公平。這會不會根本整個就是大公司或是老闆、業主的陰謀??? 讓大家分享,鼓吹大家分享,然後降低軟體開發成本? 呵呵,我怎麼會知道,你自己覺得呢?

但我最後要說的是,不管有利或不利,這就是一個趨勢。前陣子引用Ruddy老師說過的話,一位信仰敏捷的人,是會認清這個事實:「你無法凍結需求,正如你無法凍結市場、競爭、知識、進化或者成長一樣。」簡單的說,不管你願意不願意,我們就是正邁向一個每個人都能寫Code的世界。 你...擋...不...住...的

如果你不學習,不試著提升自己(這個學習和提升,指的絕對不只是軟體開發這個領域),或許下一波挑戰來臨時,軟體開發人員將面對如同過去傳產從業人員所面對一樣的大規模失業問題...

人生的挑戰,從來沒有消失過。

2013年10月14日 星期一

MS Enterprise Library 6.0 (四) - Policy Injection Application Block

如果說Unity Application Block可以算得上是一份美味的佳餚,那 Policy Injection Application Block 絕對稱得上是開發人員最華麗的盛宴了。

要介紹Policy Injection Application Block之前,得提醒讀者,建議您先熟悉先前介紹過的Exception Handling Application Block以及Logging Application Block,因為我們待會要利用 Policy Injection Application Block 來作一個應用大集合。

首先,請你回憶一下Exception Handling Application Block,以及想像一下Exception Handling Application Block會怎麼用在你的系統中,以及怎麼幫助你以一個單一的例外處理模塊來管理你開發的系統的Exception...

不要偷懶,想像一下...
...
...
...
...
...

好,接著我們回到現實面,你會發現,我們前面說過,可以透過Exception Handling Application Block來管理例外,提供一個統一的方式來處理各種不同可能的例外...這樣很好,我們的系統透過這樣的設計,已經大幅的將耦合性降低,並且提高了重用性,這樣還有什麽讓人不滿意的地方呢?

有的,當程式碼愈趨鬆耦合時,我們開始看try...catch不太順眼。此話怎說??? 請看下面這塊典型的try...catcah:
public void MethodA()
{
    try
    {
        int a = 10, b = 0;
        a = a / b;
    }
    catch (Exception ex)
    {
       //以Config檔案中的 "Policy" ExceptionPolicy設定來處理
       var rethrow = ExceptionPolicy.HandleException(ex, "Policy");
       //如果設定檔說要重丟例外,就重丟
       if (rethrow) throw;
    }
}


有沒有發現,雖然我們簡化了catch當中的程式碼,但是如果我們仔細思索,會發現try...catch這個機制,幾乎會出現在每一個可能發生例外的method當中,也就是說,如果我們寫的嚴謹一點,try...catch幾乎在每一個重要的method當中都會出現。

(我知道你可以在某些開發環境中透過Global的例外處理模塊來處理,例如ASP.NET的Application_Error,但為了解釋方便,請先當作這個機制不存在....)

也就是說,我們的程式碼可能開始會變成這樣
private void MethodA()
{
    try
    {
        //Business Logic block
        //Business Logic block
        //Business Logic block
    }
    catch (Exception)
    {
        //完全一樣的exception Handling block
    }
}

private void MethodB()
{
    try
    {
        //另一段Business Logic block
        //另一段Business Logic block
        //另一段Business Logic block
    }
    catch (Exception)
    {
        //完全一樣的exception Handling block
    }
}

有沒有發現,每一段Method當中,都有除了Business Logic以外,還有著其他很多其實並非核心的運算邏輯(例如上面的catch區塊...),仔細想想,這樣的情況還非常多,在Method核心的代碼中,總是有一堆跟核心邏輯無關,卻又無法不寫的程式碼,有哪些? 可多了,諸如:權限檢查(permission)、參數檢查(Validation)、例外處理(Exception Handling)、日誌紀錄(Logging)...etc

這些都是撰寫程式碼必須的,但卻跟我們的Business Logic不全然直接相關,這些我們稱為"Infrastructure Logic",在架構上屬於cross-cutting concerns。

這類的程式碼,若能與Business Logic切割開來,能夠立即得到許多好處,諸如:
  1. Infrastructure Logic和Business Logic耦合性降低,兩者分別後更容易重用
  2. 程式碼維護變的更加容易,開發時也可以更專注在Business Logic,不用分心去管雜七雜八的其他程式碼
  3. 程式碼可讀性也變高,閱讀時不會被不相關的code干擾...
  4. ...
總之,似乎好處多多。但由於您可能還沒有辦法理解我們待會要怎麼分割兩者,因此你現在可能完全沒感覺,不急,繼續看下去就會慢慢理解。

=============================================

好,剛才說到,要把程式碼中的"Business Logic"與"Infrastructure Logic"切開來,但是要怎麼切呢? 請回頭看一下上面的那段程式碼:

public void MethodA()
{
    try
    {
        int a = 10, b = 0;
        a = a / b;
    }
    catch (Exception ex)
    {
       //以Config檔案中的 "Policy" ExceptionPolicy設定來處理
       var rethrow = ExceptionPolicy.HandleException(ex, "Policy");
       //如果設定檔說要重丟例外,就重丟
       if (rethrow) throw;
    }
}


基本上,在上面這段程式碼當中,Business Logic只有a=a/b那一塊(雖然只是範例,一點用處都沒有,而且必錯無疑),但為了要預防例外,我們加上了try...catch,而這就是我們所說的 Infrastructure Logic 。

這造成了程式碼之間不必要的耦合性,如果可以切開,那不是挺美好的? 要怎麼切? 有一個美麗的答案,就是... attribute 機制。

想像一下,如果程式碼變成底下這樣呢?
        [ExceptionCallHandler("Policy")]
        public void MethodA()
        {
            int a=10, b = 0;

            a = a / b;
        }
這樣會不會好多了?

我們把原本的try...catch完全拿掉,透過attribute的形式取代,程式碼的效果完全相同,但難看死了的try...catch已經完全消失,開發人員在寫code時,可專注在這段 a=a/b 的Business Logic,是不是清爽很多? 是不是把程式碼中的"Business Logic"與"Infrastructure Logic"切割開來了?

OK,要如何完成這樣的功能? 這就是Policy Injection Application Block的應用"之一"了。

Policy Injection Application Block,讓我們可以在Method上加上attribute(其實還可以透過config檔案來設定,但我們先講attribute,因為我比較喜歡attribute),然後透過attribute來賦予method一個新的功能,來形成Policy Injection。(如果有需要,開發人員也可以自行實作自己的attribute)

==========================================
具體的做法非常簡單,首先,請先在專案中利用NuGet加上幾組Enterprise Library套件:
  1. Policy Injection Application Block
  2. Exception Handling Application Block
  3. Exception Handling Application Block Logging Handler
  4. Logging Application Block
其實本來只需要 1.,但我們要示範加上attribute來處理exception之後,要把exception寫入Log File,所以又加上了2,3,4。

完成後,請撰寫底下程式碼(Console Application):
    class Program
    {
        static void Main(string[] args)
        {
            //抓取Config檔案中的設定,作為configurationSource 
            IConfigurationSource configurationSource = ConfigurationSourceFactory.Create();
            //以Config檔案中的設定,建立logWriterFactory 
            LogWriterFactory logWriterFactory = new LogWriterFactory(configurationSource);
            //以Config檔案中的設定,建立LogWriter
            Logger.SetLogWriter(logWriterFactory.Create());
            //以Config檔案中的設定,建立ExceptionManager
            ExceptionPolicy.SetExceptionManager(new ExceptionPolicyFactory(configurationSource).CreateManager(), true);
            PolicyInjection.SetPolicyInjector(new PolicyInjector(configurationSource));
            //故意寫一段會錯的程式碼
            ClassA ClassA = PolicyInjection.Create< ClassA >();
            ClassA.MethodA();
        }
    }

    class ClassA : MarshalByRefObject
    {
        [ExceptionCallHandler("Policy")] //請注意其中的Policy是個參數
        public void MethodA()
        {
            int a=10, b = 0;

            a = a / b;
        }
    }

注意第20行的ClassA,是繼承自MarshalByRefObject。

回到主程式Main當中的前幾行(5-13),我們先前介紹其他Application Block的時候曾經介紹過,基本上就是透過抓取Config檔案中的設定,來建立PolicyInjector, ExceptionManager, LogWriter...等類別。

比較需要注意的是,15行建立類別ClassA實體(instance)的程式碼被改為透過PolicyInjection來建立,至於後續的用法則完全相同。(也就是說,本來你是直接new一個ClassA,現在改成透過PolicyInjection來建立,這有何差別? (先賣個關子,只能說,裡面用到了Unity Application Block,生成出來的ClassA其實也不是以前那個單純的ClassA了...)

  OK,注意第22行,我們在MethodA上頭加上了Attribute,也就是賦予了MethodA()另一個行為,或許可以看成把Policy注入(Injection)到MethodA身上。如此一來,MethodA()就神奇地有了attribute所描述的功能(什麼功能? 當然就是exception Handling)。

如此一來,你不需要在MethodA()當中撰寫try...catch,而這個method發生例外時,自動會把例外導向到Config設定檔中,參數Policy指向的錯誤處理機制。(如果你不太有印象,回頭看看MS Enterprise Library 6.0 (三) - Exception Handling Application Block 那篇)

請留意,上面這段code要能夠順利執行,除了透過NuGet引用Application Blocks之外,還要設定config檔案,至於如何設定,也請參考MS Enterprise Library 6.0 (三) - Exception Handling Application Block 這篇,因為幾乎一模一樣。

===========================================
到這邊,有瞭解了嗎?

基本上,Policy Injection 讓我們以注入的方式,透過設定檔或是attribute,把特定行為注入(加到)某一個method或某一個object身上,這讓我們前面說到的Infrastructure Logic和Business Logic可以切割開來,砍斷其耦合性,便於個別的重用與維護,也讓開發變得更加簡單。這種把Business與cross-cutting concerns分隔開來的概念,有個經典的名字,稱作AOP - Aspect-Oriented Programming,有興趣的朋友可以上網找找。(PIAB嚴格說起來不稱做AOP框架,但基本有實現這個精神)

這麼好? 有沒有代價? 有的...不過既然你不知道,就暫時先不提了。

這個好用的機制,是否有可能客製化呢? 例如我自己發明一兩個attribute,掛上這個attribute,某個Method就自動支援身分驗證檢查? 或是支援count? Log? Validation? Cache? ....

其實都有,上面這些很多在Enterprise Library當中早有現成的,不過也有一些從6.0開始被移除,也有一些你必須自己來繼承ICallHandler實作。

但這些...就等到下次有空再說了...

2013年10月13日 星期日

MS Enterprise Library 6.0 (三) - Exception Handling Application Block

有時候心情好,忍不住就多寫幾篇Blog。(當然,心情比較差的時候,可能十天半個月沒辦法安安靜靜的把一些東西整理出來)

前面錄了兩段Enterprise Library的介紹影片,分別談了Unity Application BlockLogging Application Block,這一篇緊接著談Exception Handling Application Block。

由於時空的限制,我現在所在的位置沒法錄製影片,所以只好乖乖用寫的了。

如果可以讓我選擇,我喜歡錄影片遠超過寫文字,原因很簡單,因為我話多,blog有時候忍不住就多寫了幾百字,但這年頭大家都不看字的,害我覺得寫了很無聊。寫了沒人看不打緊,花的時間很多,年紀大了越來越懶,用講的比較快,還可以當作上課的彩排甚至教材,所以我喜歡錄影遠超過寫文章。

岔題了。

Exception Handling Application Block,顧名思義就是系統中處裡例外的模塊,大家都知道,我們會在程式碼中加上try...catch來處理例外,程式碼常常會像是這樣:
            try
             {
                 int a = 10, b = 0;

                 a = a / b;
                 Console.WriteLine("done!");
                 Console.ReadKey();
             }
             catch (Exception ex)
             {
               //如果發生例外...就...
             }
這樣的程式碼沒啥太大的問題(除了因為是範例所以一定會發生例外之外...)。

但如果像上面這樣的程式碼一多,我們就會發現,每一個try...catch裡面都是另一段不受管理的複雜的錯誤處裡邏輯,可能是寫log、可能是發mail、可能是retry...,不僅如此,一旦例外處理區塊寫死之後,例外處理區塊常常本身就是一個大累贅。

還有,發生例外的可能性和類型很多元,上面這段程式碼非常簡單,只可能得到一個 "除以零" 錯誤。但真實世界中,如果是一段比較複雜的程式碼,有網路連線、有檔案處理、有數字計算、日期計算...而我們要針對可能發生的不同的例外分開來處理(例如發生檔案相關錯誤就發mail、發生網路錯誤就寫file log,發生其他運算錯誤就寫event log...,那你的catch中的程式碼就精彩了!!!
             try
             {
                 //如果這邊很長,或是進行了各種不同的動作             }
             catch (Exception ex)
             {
                 //如果是DivideByZeroException
                 if (ex.GetType().Equals(typeof(System.DivideByZeroException)))
                 {
                     //...
                 }
                 //如果是WebException
                 if (ex.GetType().Equals(typeof(System.Net.WebException)))
                 {
                     //...
                 }
                 //如果是FileLoadException
                 if (ex.GetType().Equals(typeof(System.IO.FileLoadException)))
                 {
                     //...
                 }
             }
所以可以想見,當我們在真實世界的專案當中,catch部分可能要針對各種不同型態的例外,進行各種不同的處理,再加上又要考慮是否rethrow,複雜度又更高了...

也因此,我們需要一個一致性的例外管理模塊,這時候, Exception Handling Application Block 就可以派上用場了。Exception Handling Application Block 可以做到:


  1. 用統一的程式碼來管理例外
  2. 透過設定App.Config來處理例外,如此一來若要改變例外處理的方式,就可以不用改程式碼
  3. 可針對不同類型的例外設定不同的處裡方式
  4. 可隨時增加或減少某種例外的處理方式(例如發生例外時的紀錄本來只有發email,但要改成同時寫入db,可以透過設定的方式來進行)
知道了用途之後,我們就來看看實際上怎麼用。

Step1:現在專案中加入:
  1. Exception Handling Application Block
  2. Exception Handling Application Block Logging Handler
  3. Logging Application Block

本來我們只需要加入第1個,但因為我們處理Exception的方法是寫Log,因此也加入了2,3



Step2:請參考底下的程式碼:
底下這段程式碼是Console Application,基本上3-10行是Enterprise Library的相關程式碼,主要是從設定檔來建立所需要的各種物件。(待會會教大家如何建立設定檔)
16行是會觸發一個除以零錯誤的代碼
而23,24則是我們的核心了,我們透過ExceptionPolicy這個 enterprise library的錯誤處理類別,來處理錯誤,採用的是名稱為Policy的設定檔內容。

-------------------------------------------------
static void Main(string[] args)
{
    //抓取Config檔案中的設定,作為configurationSource 
    IConfigurationSource configurationSource = ConfigurationSourceFactory.Create();
    //以Config檔案中的設定,建立logWriterFactory 
    LogWriterFactory logWriterFactory = new LogWriterFactory(configurationSource);
    //以Config檔案中的設定,建立LogWriter
    Logger.SetLogWriter(logWriterFactory.Create());
    //以Config檔案中的設定,建立ExceptionManager
    ExceptionPolicy.SetExceptionManager(new ExceptionPolicyFactory(configurationSource).CreateManager(), true);
    //故意寫一段會錯的程式碼
    try
    {
        int a = 10, b = 0;

        a = a / b;
        Console.WriteLine("done!");
        Console.ReadKey();
    }
    catch (Exception ex)
    {
        //以Config檔案中的 "Policy" ExceptionPolicy設定來處理
        var rethrow = ExceptionPolicy.HandleException(ex, "Policy");
        //如果設定檔說要重丟例外,就重丟
        if (rethrow) throw;
    }
}
--------------------------------------------------------

OK,所以瞭解了程式碼之後,就知道關鍵在名稱為Policy的設定檔內容,這個設定檔的內容決定了我們要如何處理、以及要處理哪些類型的例外,並且決定了處裡完例外之後,是否要重新丟出例外。

知道了之後,我們就來看如何建立這個設定檔,請參考底下的影片,我們的動作是:

  1. 從VS2012開啟設定檔編輯器
  2. 建立Exception Handling Settings( 注意名稱為 Policy,且post HandlingAction設為None )
  3. 建立Logging Exception Handler(配合Exception Handling Settings)
  4. 在Logging Exception Handler中,指定Logging Settings( 注意名稱為 General )
  5. 建立Rolling Flat File Trace Listener


最後,我們來看執行結(請留意,當修改了設定檔之後重新執行,你會發現程式對錯誤的處裡方式也隨著設定檔而蓋變了):

btw,   請原諒我只能很扼要的介紹Enterprise Library中的exception handling application block,其他更多精彩的功能,請自行參考微軟Enterprise Library網站囉。

MS Enterprise Library 6.0 (二) - Logging Application Block

Logging Application Block,其實是相當簡單的機制,基本上就是把紀錄Log這件事情,設計成一個獨立的模塊,讓開發人員可以方便重用。
 
Enterprise Library當中,方便好用的地方在於,已經內建了多種Listener供開發人員使用,讓你將Log寫入各種不同的位置,諸如Event Log, File, DB, eMail...etc,如果有需要,您也可以自行繼承建立新的Listener,衍生出新的紀錄輸出位置。
 
要如何利用Enterprise Library中的Logging Application Block,輕易的在程式碼中透過單一的方式寫入Log,並且支援可隨時透過設定(不需要調整或修改程式碼)即可動態調整Log寫入位置,或增減各種不同的Log存放位置呢?

底下這段影片可提供您參考。 如果你的網速可以,建議以720p解析度觀賞:
( BTW, 如果你覺得我前面簡介Enterprise Library講得太囉嗦了,可以從 3:38 開始看Logging Application Block)
 
 

2013年10月12日 星期六

MS Enterprise Library 6.0(一) - Unity Application Block

開發人員在自己很辛苦的寫Code的同時,如果有時間,不妨多參考一些經典的套件或組件,從其中不僅可以學習到如何更快速有效的開發出中大型系統,同時間也可以參考前人的智慧,吸取他人的經驗,用來開發自己(公司)的套件或函式庫。

每每上課時,聽到開發人員撰寫代碼的重用率,幾乎都低到不行,即便專案的形似類似,卻常常重寫系統,不免有些感慨。如何能夠讓開發人員以更少的時間,完成更有價值的工作成果,實在是開發人員應該要關注的技巧。

底下這段影片,是答應學員要錄製的,內容來自我在今年(2013)Microsoft Techdays Taiwan研討會上介紹Enterprise Library的一個小片段,以一個具體的HR系統,薪資計算作為實例,來討論Unity Application Block的具體應用,當然,這個的背後就是IoC(Inversion of Control)與DI(Dependency Injection)的實現。

研討會中,總是用飆車的速度把原本要講超過半小時的東西,在5-10分鐘講完,對學員來說難免有些囫圇吞棗,導致吸收不良,有機會我盡可能把研討會中的內容,再整理成影片分享。

這段影片錄的不是很理想,甚至有些解釋不是很到位,但請原諒我時間真的不夠,沒能抽出時間再剪接修改(我的影片從來都是一次錄完,中間失敗了就整個重錄),只好先以這個版本拋磚引玉一下,希望大家別介意。跟以前一樣,如果你覺得影片的內容對你有些幫助,就多一點分享給其他人,如果有需要改進的地方,請直接mail給我囉。

 如果你的網速可以,建議以720p解析度觀賞:
( BTW, 如果你覺得我前面講得太囉嗦了,可以從 5:24 開始看實例 )