顯示具有 infoQ 標籤的文章。 顯示所有文章
顯示具有 infoQ 標籤的文章。 顯示所有文章

2009年4月18日

[InfoQ QCon] 10 ways to improve our code

早上看了這個 talk (InfoQ Video link, Summary),主題是講 10 個改善程式碼品質的原則,有些不錯的地方值得記錄一下。

主講人 Neal Ford 先生是 ThoughtWorks 的架構師(跟 Martin Fowler 同一家公司),
演講的內容是從他寫的書:The Productive Programmer 裡面整理出來的,他講的很流暢,也蠻幽默的,演講時間大約一小時,投影片可以在這裡下載。

由於重點在 Summary 裡面都有列出來了,因此以下採用補充的形式記錄我印象深刻的部份,請搭配 Summary 服用。

Composed Method & SLAP (Single Level of Abstraction Principle)
Composed Method 這個原則講的是透過 method 的切割,盡可能的縮短每個 method 的行數,最後完整的 class 會由很多小小的 method 組成。

以切割 method 的方式來縮短行數的目的在於將程式碼的權責劃分清楚,以便達到最大程度的 reuse。但是在設計上也不應該為了切割而切割,此時可利用 SLAP 作為切割 method 時的 guideline。

SLAP 看來是 Neal 發明的縮寫,在 Wikipedia 上目前查不到,原始出處就是他寫的 The Productive Programmer,另外可以參考 Microsoft Architect Journal 2004 年 7 月號:Secrets of Great Architects(Levels of Abstraction: A Powerful Weapon for All Engineers)。

簡單的說,程式碼本來就是很抽象的,但還是可以區分出不同的抽象層次。最粗略的分法是,對 DB 的操作歸類為系統中較底層的程式碼,而 Domain Logic 則是系統的核心,UI 則是系統與使用者互動的介面,因此這三個層次的程式碼不應該混淆在同一個 method 中,就算不抽離到其他的 class 中,起碼也要拆解成不同的 method。

以下是同一個 method 中包含多個抽象層次的 pseudo code(節錄自投影片):
addOrder(WithMixedLevelOfAbstraction)

在這個未經 refactor 的版本中,可以看到非常詳細的操作 DB、Transaction 的程式碼,跟此 method 要處理的「新增訂單」Domain Logic 混雜在一起,因此閱讀的時候你的”心智”(mind)要多次在不同的抽象層次間的切換(mental shift),這種額外的負擔會造成閱讀程式碼時的不順暢。

以下是 refactor 之後的結果:
addOrder(WithSeparatedLevelOfAbstraction)

經過 refactor 後程式碼明顯縮短很多,而且現在此 method 中的抽象層次(大致)是一致的,如「completeTransaction」、「rollbackTransactionFor」等等的 method 不但名稱更有 business 上的意義,也隱藏了與 business 無關的、底層 API call(操作 DB、Transaction), 因此這段程式碼閱讀起來比上一個版本順暢很多,一行一行的順著讀下來比較接近日常生活的會話
(撰寫此 method 的目的:新增訂單)
  1. 建立所需的 DB Connection 和 SQL Statement。
  2. 開啟 Connection 和準備 SQL Statement。
  3. 設定 transaction 的狀態。
  4. 新增一筆訂單。
  5. 設定訂單號碼。
  6. 將購物車中的物品資料寫入訂單。
  7. 完成交易。
Question Authority
這個原則中文應該可以稱為「挑戰權威」吧!例如挑戰 coding convention,或者(公司內)常見的系統設計方式。Neal 舉的例子是一個有名的猴子實驗,我找到一篇文章:The Origin of Tradition: A Monkey Experiment (圖文說明),另外很久之前在書上看過一篇類似的文章,我自己打字以後做成 PDF 檔,可以在這裡下載(這都是文字,沒有圖,但是內容比較充實)。
比較有趣的是上面那篇我另外找的文章裡面有引述愛因斯坦的話:

Only two things are infinite, the universe and human stupidity, and I’m not sure about the former.
                                                                                               -Albert Einstein

建立 convention 對 productivity 是一件好事,但是深入的了解 convention 形成的原因,才有辦法在適當的時候打破 convention,以新的方法做事情,創造更大的價值!

詳細內容就不贅述了,不管這是真的實驗還是一個故事,總之很發人深省!

Use the Anti-Objects Pattern
在這裡 Neal 利用早年大型機台上的老遊戲 – Pac-Man(中譯:小精靈)來解釋何謂 Anti-Objects Pattern。在 Wikipedia 上有個 entry 就叫做 Antiobjects,剛好裡面就有講到 Pac-Man(警告!以下資訊可能會破壞你對小精靈這款遊戲美好的回憶 XD)
The notion of antiobjects is a computational metaphor useful to conceptualize and solve hard problems by swapping computational foreground and background.
If we implement, as part of a Pac-Man game, a ghost we are tempted to think of the necessary behavior associated with the ghost object.
Antiobjects turn things on their head. In the case of Pacman we put the main computation into the maze.
                                                                                                            - Wikipedia


在當年運算能力十分缺乏的平台上,是無法將「追逐小精靈」的 AI 設計到「鬼」這個 object 上的(由鬼來計算迷宮中的 shortest path ),因此反過來設計者把 AI 設計到迷宮的地板上去,並且提出一個 Pac-Man Scent(小精靈的氣味)的觀念,也就是目前小精靈所在的那塊地板氣味最強,而這個氣味是交由迷宮的地板來記錄的,當小精靈遠離這塊地板時,它的氣味就逐漸減弱。

因此鬼實際上沒辦法知道小精靈的位置,鬼都是漫無目的的瞎逛,若剛好踩到一段有小精靈氣味殘留的地板,才會一路往氣味更強的方向去「追殺」小精靈。

透過 antiobjects 可以簡化 AI 的設計,另外也是訓練你如何 think outside of the box 的好方法喔!

最後來回憶一下經典的小精靈遊戲畫面吧!
6

2009年4月14日

John Resig: The DOM is a Mess (and the inspiration I got from this talk)

這個 talk 約 1 小時 12 分,推薦給對 jQuery/JavaScript 有興趣的人,對於開發 Framework/API 的人來講也有很多可以借鏡的地方。(線上觀賞影片在這裡)

John Resig 是目前很紅的 Open Source JavaScript Library – jQuery 的發明人,
他任職於 Mozilla Corporation,這場 talk 是他分享在開發 jQuery 的過程中遇到的困難以及心得感想,對我來說蠻有啟發性的,雖然 scope 差了十萬八千里,但我維護的元件性質也類似 jQuery,也會被很多 client 以各種意想不到的方式使用,也要面對 backward compatibility、graceful degradation 等等問題。

以下是我聽到的一些重點:

Graceful Degradation


這裡有個定義,在 web browser 這個領域裡面,很常見的 reference 是 YUI Graded Browser Support

這個概念講的是由於實務上的限制(時間、金錢),(JavaScript) framework 沒辦法 support 快速變化的各家 browser 的各個版本(的各個 mode),因此要有一套 support 的策略,細節包括各家、各版 browser 的市佔率,以及要 support 所必須付出的成本(這通常跟 browser 遵守 standard 的程度成正比)等等。

除了要制定一套策略,程式的寫法也要注意,以下是從影片中節錄的圖(有點糊@@)
ResigSpeech_GracefulDegradation(EventBinding)

這裡的程式寫法的順序大有學問,首先在 if statement 裡面試圖用標準的 W3C 語法來附加 EventListener,但是並非所有 browser 都有實作 W3C Spec,因此針對 IE 這個尚未實作 W3C Spec 的 browser 的 API Call 就寫在 else if 裡面。

這樣有什麼好處呢?當未來 IE 實作了 W3C Spec 之後,這個 attachEvent function 的程式一行都不用改,IE 自然可以執行到 if statement 中的標準 W3C 語法!而遇到還沒實作 W3C Spec 的 browser 也還是可以正常的執行這個 function(只是經過額外的判斷,效能稍微差了一點點)。

結論就是:愈符合標準/愈新的 API Call 應該寫在越前面,vendor-specific 的 (non-standard_ API Call 應該要往後挪,如此才可減少未來升級的 effort。

Feature Simulation


接下來一個重點是如何判斷目前執行這段 js 的 browser 是否有提供特定功能?
 
最早的時候都是透過 browser sniffing/user agent sniffing 的技術,但是這樣的寫法太沒有彈性,每次 browser 推出新版的時候,程式可能都需要修改。甚至當網站被攻擊的時候,可能 header 中的資訊會被竄改(或者 power user 自己修改),導致 js 判斷錯誤,進而產生了奇怪的畫面等等。

比較好的程式寫法如下圖所示(還是有點糊@@)
ResigSpeech_FeatureSimulation(Verify API)


前半段的程式 create 了一個 div,然後在這個 div 上加上 comment,最後 return getElementByTagName(“*”) 的長度是否為零的 boolean 值。根據這個值就可以知道目前的 browser 對於 getElementByTagName 的 implementation 是否有 bug(一切以 W3C Spec 為準),而接下來的程式就可以根據這個 boolean 值做適當的處理。


這裡的觀念類似前面講的 Graceful Degradation,重點在於不要根據 browser/user agent 的版本來假設目前 browser 對某個 API (e.g., getElementByTagName) 的 implementation 為何,實際上用程式測出來的結果才是最準的!
(這也許可以算是 unit test 的進階/變形應用?)


Reduce the number of assumptions


這其實是比較一般性的通則,在程式裡面通常都會有很多假設(assumptions),
很多時候寫程式的人不會特別去注意這些假設,但是就如同去年 MS TechEd 的心得
當假設出了問題,或許是假設不符合實際情況,或者是被駭客攻破(e.g., 設法製造一個出乎你意料之外的 input),那麼系統就會出很大的問題。因此盡量設法減少系統中的假設是一個很好的 practice,系統的假設少了,出錯的機會就少(因為可能要多寫程式去處理一些特殊狀況)

但是適量的假設是有必要的,例如你很難(大概也沒有必要)對系統中每一個變數都額外再寫程式去驗證這個變數的 type 是否正確,這基本上是 type system 的工作,因此「type system 要負責抓到如 InvalidCastException 這樣的例外狀況,並停止程式的執行,以免程式真的 run 下去之後導致更嚴重的錯誤(buffer overflow?)」就是一個典型的假設,此時通常都需要信任這個 programming language 所提供的 infrastructure 是夠嚴謹的,而不是自己寫一大堆程式到處去做 type checking。

今天不知道為什麼突然想寫寫看這樣的心得,大概是因為最近類似影片看很多,
以後有空應該會多寫一點類似的文章 :p

Google Spreadsheet 裡用規則運算式

最近因為工作關係,遇到要用 Google Form 及 Google Sheet 所以研究了 Google Sheet 裡的一些 function 怎麼用 首先,分享一下如何在 Google Sheet 裡用規則運算 :D