l

2012年11月30日 星期五

Structural Patterns要解決什麼問題(上)?

Nov. 22 16:00~18:00
螢幕快照 2012-11-22 下午5.46.58

在談完「Creational Patterns要解決什麼問題」之後(請參考《上集》、《中集》、《下集》),接下來分析一下structural pattern要解決的問題。延續之前在探討「Creational Patterns要解決什麼問題」的時候Teddy所想到兩個問題,將這兩個問題套用在structural pattern上面:
  1. 這7個structural pattern到底是要解決什麼問題?
  2. 它們之間的差別是什麼?
首先看一下GoF書中對於這7個structural pattern的Intent:
  • Adapter: Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.
  • Bridge: Decouple an abstraction from its implementation so that the two can vary independently.
  • Composite: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
  • Decorator: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
  • Facade: Provide a unified interface to a set of interfaces in a subsystem. Façade defines a higher-level interface that makes the subsystem easier to use.
  • Flyweight: Use sharing to support large numbers of fine-grained objects efficiently.
  • Proxy: Provide a surrogate or placeholder for another object to control access to it.
仔細一看這7個pattern的Intent都蠻像是Solution的,要找出這7個pattern所要解決的問題,就必須自己歸納一下。不過其實也不用自己歸納,在GoF的書中,有幫每一類的pattern做了一些分析比較,請翻開GoF第137頁的第一句:
Structural patterns are concerned with how classes and objects are composed to form larger structures.
把這句改寫一下應該就OK了。
Problem:How do you compose classes and objects to form larger structures? (你如何組織類別與物件以產生更大的結構?)
這個問題描述算是一個很一般化且共通的問題,可以適用於這7個structural pattern。但是,光是這樣不足以區隔這7個pattern,也就無法推導出針對這7個pattern,要採用哪種特定的Solution。請問鄉民,如果這7個pattern的問題都一樣,要如何區隔它們?如果有看過這系列文章的鄉民,這個問題的答案應該很清楚了:從個別pattern的Force可以演化出不同的Solution
***
今天介紹第Adapter、Facade、Proxy這三個pattern的Force,是何種Force導致我們選擇不同的Solution。
使用Adapter
  • 你想要使用一個已經存在的類別(且/或者包含這個類別的子類別),雖然這個類別在功能上符合你的需求,但是它的介面和你所期待的並不相容。
  • 你想要設計可被重複使用的類別,希望這個類別可以跟不相關或是未知的其他類別一起合作。也就是說,你希望類別之間既使介面互不相容也可以一起合作。
使用Facade
  • 你想要減少子系統與客戶端程式的相依性,如此一來當子系統內部改變的時候,它的客戶端將不會受到影響。
  • 你希望子系統可以很容易地被使用。。
使用Proxy
  • 你有一個功能已經實作完成的物件。
  • 因為一些非功能面需求,例如減少記憶體使用、增加操作性能,可想要控制物件的產生、初始化、存取控制等cross-cutting concerns。如果直接修改原本的物件來加入這些控制將會違反基本的物件導向設計原則。
  • 為了達到資訊隱藏,物件的客戶端應該不需要知道物件的內部實作。因此,不管物件是因為需要而及時被產生,或是物件是否位在本地端或是遠端這類的問題,客戶端應該不需要知道。
***
看完這三個pattern的force,再回頭思考原本的問題:你如何組織類別與物件以產生更大的結構?
  • Adapter告訴鄉民們,若現有的類別或物件在功能上是符合所需的,只是因為介面與你所期待的並不相容,則可以藉由Adapter這個模式,來組織類別與物件,以便產生一個更大的結構。要如何藉由Adapter組織一個更大的結構,則是要參考Adapter的Solution,今天是探討Problem與Force,就不談Solution了 熱戀
  • Facade告訴鄉民們,你有一個子系統,現在有客戶端的程式想要使用這個子系統(所以你的系統結構變大了…這不是廢話嗎 挑眉質疑)。如果你希望客戶端程式不要受到子系統內部物件結構或關係改變的影響,而且希望客戶端程式可以很容易地使用這個子系統,則可以套用Facade。
  • Proxy告訴鄉民們,有一個功能已經OK的物件,你希望對這個物件增加一些cross-cutting concerns的控制(例如物件產生、初始化、存取控制、保護等)。你最好不要直接去修改原本的物件,而是可以透過Proxy(增加一個新的類別),來達到這些控制的目的。這個Proxy會包含原本那個已經存在的物件,所以產生一個新的、更大的結構(這樣解釋會不會有點牽強 挑眉質疑)。
***
還是要再打一下廣告,「Design Patterns這樣學就會了:入門實作班」開課日期改到2013年1月19、20、26日(週六、日、六)。對設計模式有興趣的鄉民們可以參考一下 XD。
***
友藏內心獨白:最近可能會有很多置入性行銷的文章 挑眉質疑

2012年11月29日 星期四

為什麼要開自省會議?

Nov. 28 15:47~17:00

image

 

為什麼每個sprint結束,開發團隊都要舉辦一個Retrospective Meeting(自省會議)?」如果鄉民們不知道什麼是自省會議,請參考《Scrum 是什麼(9):Retrospective Meeting》、《Retrospective Meeting = 許願池》、《再忙,也要跟你開個自省會議》。

對於知道自省會議用途的鄉民們,應該能夠反射性地說出這句話:「自省會議的目的就是要提供團隊一個反省與檢討軟體開發流程的機會」。

***

幾天前Teddy讀了洪蘭女士的一本書,書中提到心理學家為了探討除了人類以外,動物是否有「自我認知」的能力(知道鏡子中的影像只是自己的反射),因而設計了一個實驗。心理學家把一隻黑猩猩關在籠子裡面,然後在他身旁放了一面大鏡子。幾天之後把黑猩猩給麻醉,然後在黑猩猩的臉上點了一顆紅色的痣。等黑猩猩甦醒之後一照鏡子,黑猩猩的第一個反應就是用手一直搓自己臉上的那顆紅色的痣,想把它給搓掉。

image

黑猩猩內心獨白:是誰偷偷在老娘的臉上點了一顆三八痣啊。

 

但是,如果心理學家沒有先放鏡子在黑猩猩的籠子旁邊,而是先麻醉黑猩猩並且在牠的臉上點了一顆紅色的痣,然後再放鏡子,那麼黑猩猩在甦醒之後便會以為鏡子裡面的黑猩猩是另外一隻臉上有紅痣的黑猩猩,而非自己。

如果Teddy沒記錯的話,書中提到這種「自我認知」的能力除了人類以外,只有黑猩猩具備這樣的能力。知道人類具備「自我認知」的能力有什麼用?書中提到另外一個例子。有一棟大樓的一樓電梯控制開關經常損壞,因為在一樓等電梯的人,常常會動手一直去按電梯的按鈕。即使電梯向上或向下的燈已經亮了(代表有人已經按過電梯了),在電梯前等待的人還是會經常忍不住狂按按鈕。

image

大家應該都有狂按電梯按鈕的經驗吧。

 

為了解決這個問題,大樓管理單位想出一個辦法,就是找一位工作人員站在一樓的電梯前幫忙按電梯,但是這樣做的成本太高了。後來這件事被一位心理學家知道,他用一個非常簡單的方法就解決了這個問題。什麼方法?就是在電梯前面立了一面鏡子。每當人們看從鏡子中看到自己猴急狂按電梯按鈕的樣子,就會自覺不妥,而停止狂按電梯。而且在電梯前放了鏡子之後,等電梯的人變得更加有禮貌(傑克,這真是太神奇了 不要告訴別人)。

 

***

鄉民甲:以上和自省會議有何關係?

原來人們只要從鏡子裡面看到自己的行為,就會引發「自我反省(自我改善)」的反應。自省會議的目的,就是讓開發團隊可以定期「照鏡子」,反省一下團隊目前的工作模式,有哪些是好的做法應該繼續維持下去,有哪些是有待改進的地方,日後想辦法逐步改善。

你和你的團隊有多久沒有照鏡子了呢?

***

友藏內心獨白:有人家裡沒有鏡子那該怎麼辦啊 挑眉質疑

2012年11月28日 星期三

對付時好時壞的測試案例(2):Lack of Isolation

Nov. 26 17:26~18:22
image

昨天提到對付時好時壞的測試案例第一步就是要先將它們從main deployment pipeline隔離,把這些測試案例先暫時移到quarantined pipeline中。Martin Fowler接著舉出下列5種可能會導致測試案例時好時壞的問題,並介紹如何排除這些問題的方法。今天Teddy先介紹第一種Lack of Isolation(缺少隔離)。
  1. Lack of Isolation(缺少隔離)
  2. Asynchronous Behavior(非同步行為)
  3. Remote Services(遠端服務)
  4. Time(時間)
  5. Resource Leaks(資源洩漏)
Lack of Isolation
造成測試案例時好時壞的第一個兇手就是測試案例彼此互相有關係,並非全然的獨立。什麼叫做彼此有關係(缺少隔離)?例如,有A、B兩個測試案例,A檢查某個物件是否被產生,B使用A所產生的物件來驗證某些狀態。在這種情況下,A跟B就不是完全獨立的測試案例。另一個常見的例子就是好幾個測試案例共用資料庫中相同一組待測資料。理論上,每個測試案例需要重建自己的測試資料,但是有時候建立測試資料很花時間,所以為了節省時間,有時會讓好幾個測試案例共用一組測試資料。但是萬一這組測試資料不小心被哪一個測試案例給改了,其他的測試案例就可能會失敗(和公用變數所造成的問題類似)。
解決這個問題的方法有兩個常見的簡單策略:setup與clean-up,前者要求測試案例在執行之前要負責把自己所需要的測試環境建好,後者要求測試案例在執行之後,必須要將測試環境恢復到執行測試案例之前的狀態。用講得太慢,請看圖。以下兩種方法,何者比較好?執行完測試案例再恢復狀態,還是每次執行之前先建立新的狀態?
螢幕快照 2012-11-26 下午5.54.53

這兩種做法各有各的優缺點。採用setup的方式,可以確保測試案例每次執行時都有一個已知的狀態,如果測試案例執行失敗,可能是production code有問題,或是這個測試案例本身有問題(這句不是廢話喔,等一下繼續往下看就知道)。如果是測試案例有問題,有可能是因為自己的setup步驟沒有建立起正確的測試環境,或是測試案例寫錯了。採用setup的缺點,則是因為每次執行測試案例都需要產生一個新的測試環境,所以測試案例執行速度相對來講會比較慢。
若是採用clean-up的方式,在執行一群測試案例之前,只要先把測試環境設定好一次,在執行完每一個測試案例之後,只要復原剛剛有動到的測試環境即可,不需要重新建立一次全新的測試環境。換句話說,採用clean-up的方式來執行測試案例有通常會比較快。但是,clean-up有一個非常嚴重的缺點,那就是如果測試案例執行失敗,除了可能是production code有問題,或是這個測試案例本身有問題以外,還可能是別的測試案例的clean-up有問題而導致自己出錯如果是因為別的測試案例的問題導致自己出錯,那麼這樣的bug就不容易被找出來,請參考下圖。
螢幕快照 2012-11-26 下午6.13.28

換句話說,採用clean-up的方式,會導致前後緊接執行的兩個測試案例彼此有相依性,因此如果考慮到要避免產生時好時壞測試案例的機會,那麼採用setup的方式會比較好。
***
友藏內心獨白:以前經常把setup與clean-up這兩種方式混著用耶 挑眉質疑

2012年11月27日 星期二

對付時好時壞的測試案例(1):還沒痊癒,就先隔離

Nov. 26 15:33~17:08
image

平常有在寫自動化測試案例並且執行迴歸測試的鄉民們,幾乎一定都會遇到一個問題:在沒有修改程式碼的情況下,為什麼有些測試案例有時候會過,有時候不會過呢?遇到這種狀況,相信鄉民們第一個念頭一定是:難道是遇到鬼,農曆七月不是已經過了?!除了遇到鬼這個可能的原因之外,應該還有其他更科學一點的解釋吧。
在還不用查看任何資料之前,大部分有寫過測試案例的鄉民們,應該隨便都可以找出幾個嫌疑犯。例如,測試案例的相依性(測試案例必須依照特定執行順序才會全部通過,只要前面的測試案例失敗,後面的測試案例就會被「帶屎(拖累)」跟著一起失敗)而導致這種時好時壞的靈異現象。還有timing也是很常見的問題,像是點選網頁上某個連結,等待若干秒之後期待會有什麼「好事(結果)」發生,但是等到天荒地老卻什麼事也沒發生 挑眉質疑
Teddy在開發軟體的時候,也經常會遇到測試案例時好時壞的問題,也累積了一些小技巧用來解決這些問題,但是從來也沒特別想過產生這種現象的根本原因可能有那些。幾個月前無意中讀到了Martin Fowler的這一篇《Eradicating Non-Determinism in Tests》(消除測試中的不確定性)文章,發現Martin Fowler已經幫大家整理好了,那我們這些後生晚輩就可以直接站在巨人的肩膀上看得更高、更遠了 微笑
***
還沒痊癒,就先隔離
當遇到這種時好時壞的測試案例,請問鄉民們的第一個反應是什麼?直接把測試案例丟掉,還是嘗試修正?換個方式來問這個問題,假設你有一個朋友得到SARS,請問鄉民們該如何處置?人道毀滅(直接丟掉),這樣太慘忍了。想辦法醫治(嘗試修正),但是一時之間又不知道要用什麼藥可以治療SARS。搞不好藥還沒找到,就先傳染給其他人,那豈不是更慘。所以,遇到這種情況,第一件要做的事應該是把病人(有問題的測試案例)給關在負壓隔離病房(居家自我隔離也OK啦)
什麼叫做「關在負壓隔離病房」?簡單的說,就是要把病人(時好時壞的測試案例)從正常人(正常的測試案例)裡面給挑出來。因為如果不這麼做,測試案例有時成功,有時失敗,久而久之開發人員就會想:啊,我們的測試案例就是不知道為什麼時好時壞,所以這一次的執行結果雖然失敗,但「此為正常現象,請安心服用」,不用管它了。原本測試案例如果執行失敗應該是要請開發人員找出程式中的問題(production code或是test code有問題),但如果把時好時壞的測試案例和正常的測試案例混在一起,時間久了之後開發人員就不再信任測試案例,因此這些測試案例就等於沒有作用。
知道了為什麼要隔離之後,接下來說明一下要如何隔離。根據Martin Fowler的說法,隔離的意思,是把時好時壞的測試案例從持續整合系統的「main deployment pipeline 」移除。這是什麼意思?用畫圖解釋比較快。
當開發人員把程式送交到版控系統(例如,SVN或Git)之後,版控系統送一個通知訊息給持續整合系統,觸發一次建構。在正常情況之下,持續整合系統被板控系統所驅動的每次建構會執行一個主要的建構流程,稱之為「main deployment pipeline 」。因為這個流程由好幾個建構工作,例如編譯、測試、靜態程式碼分析、打包程式等等所組成,整個流程串起來好像一個管線一樣,所以稱為deployment pipeline(因為持續整合的最終目的是希望能夠佈署軟體,所以叫做佈署管線)。
螢幕快照 2012-11-26 下午5.03.35
現在問題來了,如果測試案例中有那種時好時壞的測試案例,就不宜將它們留在main deployment pipeline中。但是又不能直接砍掉,所以就在持續整合系統中額外增加一個「quarantined pipeline」(隔離管線),把時好時壞的測試案例先移到這裡隔離起來。之後,可以設定讓持續整合系統在每次執行建構的時候,分別或同時執行main deployment pipeline與quarantined pipeline這兩個管線的建構工作。這種做法的大原則是,在main deployment pipeline上的測試案例全部都要通過,而在quarantined pipeline的測試案例如果不通過則可以暫時忽略。如此一來開發人員對於main deployment pipeline的測試案例還是持續保持著信心,而至於在quarantined pipeline的測試案例,則可以找時間再來修復。
***
Quarantined pipeline不是垃圾桶
把時好時壞的測試案例移到quarantined pipeline的這種作法,有兩點要特別留意:
  • 這種做法不是教大家把所有執行失敗的測試案例不分青紅皂白全部都直接移到quarantined pipeline,只有那種因為不明原因而時好時壞的測試案例才移到quarantined pipeline。關於那種每次執行都一定會失敗的測試案例,不是production code有問題,或是test code有問題,應該要找出問題加以修正。
  • 移到quarantined pipeline只是一種暫時性權宜措施,不是把時好時壞的測試案例丟進來之後就不管它們了,還是要安排時間來修護。至於何時修正,可以由開發團隊自行決定。一種常見的做法是,鄉民們可以規定,例如quarantined pipeline裡面的測試案例最多不能超過5個,一但超過5個,團隊就必須安排時間來修復這些測試案例。修復完成的測試案例,就會從quarantined pipeline移回main deployment pipeline中。
***
友藏內心獨白:隔離和砍掉是不同的做法喔。

2012年11月26日 星期一

Creational Patterns要解決什麼問題(下)?

Nov. 21 12:00~14:20

image

***

先打個廣告,原定11月24-25、12月1日舉辦的「Design Patterns這樣學就會了:入門實作班」,開課日期改到2013年1月19、20、26日(週六、週日、週六),想學好設計模式的鄉民們請多加利用。報名網址在這裡:http://www.accupass.com/go/pattern1211

***

終於要完成這系列的第三集,也是最後一集。前兩集的內容在這裡《Creational Patterns要解決什麼問題(上)?》與《Creational Patterns要解決什麼問題(中)?》,翹課的鄉民請自行找時間補課。

在第一集中提到在Java語言至少有以下9種方式來產生一個物件:

  1. 直接在需要產生物件的時候把物件給new出來,例如 List<String> list = new ArrayList<>();
  2. 用Reflection的方式來產生物件。
  3. 用clone(Prototype pattern)的方式來產生物件。
  4. 用Singleton pattern。
  5. 用Object Pool pattern。
  6. 用Simple Factory pattern。
  7. 用Factory Method pattern。
  8. 用Abstract Factory pattern。
  9. 用Builder pattern。

前兩集已經說明了前6種產生物件的方式,今天要介紹7、8、9這三種方式,以下簡單用一個圖來說明。

螢幕快照 2012-11-21 下午12.08.05

 

接下來討論一下,是何種Force導致我們選擇不同的Solution。

使用Factory Method

  • 在設計應用程式框架或類別庫時,框架本身會使用到某一種類別或介面,但是實作該種類別或介面的物件則是由使用這個框架的人所提供的。換句話說,在設計框架的階段你只知道所要產生物件的共同父類別或介面,但不知道所要產生物件的具體類別。
  • 為了降低系統的相依性,你希望物件的使用者不需要知道產生物件的過程,只需知道該物件是由誰負責產生即可。
  • 你所要使用的物件在不同的應用中,會由使用者(使用你的框架或是軟體的人)針對相同的類別或介面提供不同的實作。

使用Abstract Factory

  • 在設計應用程式框架或類別庫時,框架本身會使用到某一組類別或介面。在設計框架的階段你只知道所要產生物件的共同父類別或介面,但不知道所要產生物件的具體類別。
  • 每組組類別或介面的通常會有一種以上的實作,不同實作方式的組別物件不可同時交互使用。
  • 為了降低系統的相依性,你希望物件的使用者不需要知道產生物件的過程,只需知道該物件是由誰負責產生即可。
  • 實作該組類別或介面的物件通常是由框架開發者提供,但也可由使用者(使用你的框架或是軟體的人)自行擴充。

使用Builder

  • 你所要產生的物件是一個較為複雜的產品物件,這個產品物件需要經由若干個較小的零件(組件)物件所組合而成。換句話說,產生這個產品物件的過程需要經過好幾個步驟才能夠完成(不是直接把所要產生的物件給 new 出來就好了)。
  • 組合出這個複雜產品物件的步驟是固定的,但是產生各種不同零件物件的實作卻可以有很多種不同的方式。
  • 在設計階段你知道組裝產品物件的步驟以及個別組件物件的介面,但是不知道產生這些零件物件的具體類別。

***

以上說明如果原本已經知道這幾個設計模式的鄉民應該一看就懂,不知道的鄉民再怎麼看可能還是看不懂 挑眉質疑。總之,最後補充幾點:

  • 在GoF的書中,Factory Method和Abstract Factory的例子都是應用在開發應用程式框架(application framework)或是類別庫(class library)。後來坊間的一些書籍或是網路上的範例,有些例子,特別是Factory Method的例子,只強調「用ConcreteCreator產生ConcreteProduct這樣的一組物件對應關係」,但卻沒有特別強調當初GoF書中是因為要設計一個應用程式框架,所以才要求使用者繼承自應用程式框架然後自行實作ConcreteCreator與ConcreteProduct。之後應用程式框架將會透過使用者提供的ConcreteCreator來產生使用者自行實作的ConcreteProduct。如果沒有這一層的需求(應用程式框架透過ConcreteCreator獲得使用者自行提供的ConcreteProduct並加以操作),那麼在許多情況下,直接使用SimpleFactory會比較簡單。
  • 除了Builder以外,其他幾種產生物件的方法,所要產生的物件本身,都是一個獨立的個體,產生之後就可以使用了。但有時候要產生的物件比較複雜,需要由較小的物件,經過若干步驟才可以產生(組裝)完畢,而且這些產生較小物件的方法還可能依據需求不同而有所不同,此時便可考慮使用Builder。

***

友藏內心獨白:看完這三集應該對於產生物件的方法與時機更加了解了吧。

2012年11月25日 星期日

2011冬遊法國巴黎Day7-A小皇宮

Nov. 12 21:45~22:29

今天原本一早9:45要搭船遊「聖馬丁運河」(Saint-Martin Canal),沒想到一到那裏,好不容易找到船家,對方居然說:「人數不足,停駛」。什麼,這可是在出發到巴黎前的好一陣子Kay就已經上網預約的遊河行程,居然一句人數不足就取消了…Orz。

螢幕快照 2012-11-12 下午6.35.42

 

這個船班一天只有兩個班次,只好等14:30再回來碰碰運氣了。

螢幕快照 2012-11-12 下午6.36.02

 

還好當初Kay已經安排好了,這附近還有另一個景點「小皇宮」可以看,那就先去逛小皇宮吧。

螢幕快照 2012-11-12 下午9.53.40

螢幕快照 2012-11-12 下午9.53.58

螢幕快照 2012-11-12 下午9.54.12

 

入內後首先看到一些雕像與花瓶。

螢幕快照 2012-11-12 下午9.55.34

 

當然也有很多、很多幅畫。

螢幕快照 2012-11-12 下午9.55.56

 

這裡面居然有繁體中文的標示 很棒

螢幕快照 2012-11-12 下午9.56.17

 

這個花瓶好漂亮啊,薄到可以透光的感覺。

螢幕快照 2012-11-12 下午9.56.33

 

繼續看畫。

螢幕快照 2012-11-12 下午9.56.50

螢幕快照 2012-11-12 下午9.57.02

螢幕快照 2012-11-12 下午9.57.24

螢幕快照 2012-11-12 下午9.58.06

螢幕快照 2012-11-12 下午9.58.17

螢幕快照 2012-11-12 下午9.58.30

螢幕快照 2012-11-12 下午9.58.56

螢幕快照 2012-11-12 下午9.59.23

螢幕快照 2012-11-12 下午9.59.36

螢幕快照 2012-11-12 下午9.59.50

螢幕快照 2012-11-12 下午10.00.08

 

逛得差不多了,也已經中午12點了,剛好小皇宮內有一個餐廳,就在這裡用餐好了。

點餐的地方,以及室內用餐場所。

螢幕快照 2012-11-12 下午10.03.59

 

戶外中庭的迴廊上也有位置。

螢幕快照 2012-11-12 下午10.04.14

螢幕快照 2012-11-12 下午10.02.54

 

午餐,印象中還算好吃啦。

螢幕快照 2012-11-12 下午10.01.28

中庭是個拍照的好地方。

螢幕快照 2012-11-12 下午10.01.41

螢幕快照 2012-11-12 下午10.01.49

螢幕快照 2012-11-12 下午10.02.28

螢幕快照 2012-11-12 下午10.03.01

螢幕快照 2012-11-12 下午10.02.40

螢幕快照 2012-11-12 下午10.03.20

 

哇,在小皇宮休息太久了,還要改回去搭船呢。

螢幕快照 2012-11-12 下午10.05.58

 

下回待續。

***

友藏內心獨白:小皇宮可以免費參觀喔。