對很多粗略接觸到 Extreme Programming 的人來說,XP 似乎 宣告了軟件設計的死刑。不只很多的設計被嘲笑為 "Big Up Front Design"[譯注1],連很多技術像UML、富有彈性的程序架構 (framework),甚至連模式 (pattern) 都不受重視,或是近似忽略了。事實上,XP內含很多設計理念,但是它與現有的軟件流程有著不同的運作方式。XP藉由多種實務技巧 (practice) 賦予演進式設計 (evolutionary design) 嶄新的風貌,讓演進變成一種實用的設計方法。它也讓設計者 (designer[譯注2]) 面臨新的挑戰與技巧,學習如何使設計精簡,如何使用重構來保持一個設計的清楚易懂,以及如何逐步地套用模式。
[譯注1] 一種在著手進行程序代碼的撰寫之前,就先按照既定的程序分析、設計、制圖、撰寫文件等等耗時費力的工作方式。
[譯注2] 在台灣你覺得 “程序設計師”、“軟件設計師” 和 “軟件工程師” 有什麼不同嗎?相信大部分的人覺得都是同一種角色。但是從英文字意就比較容易區別 “programmer”、”designer”、”architect”等等不同的角色。這樣的語言文化差異性,也是我在內文中留下不少原文的原因。在部份字句中,留下原文並不影響閱讀的順暢,但是可以避免文意因為翻譯所造成的模糊或扭曲。
(這篇文章是我在 XP2000 研討會發表的演說,它會公布在研討會講義中。)
Planned and Evolutionary Design (經過規劃的設計與演進式的設計)
The Enabling Practices of XP (XP有效的實作技巧)
The value of Simplicity (簡單的價值)
What on Earth is Simplicity Anyway (究竟什麼是簡單)
Does Refactoring Violate YAGNI? (重構違反了YAGNI嗎?)
Patterns and XP (模式與XP)
Growing an Architecture (發展結構)
UML and XP (UML與XP)
On Metaphor (關於隱喻)
Do you wanna be an Architect when you grow up? (你將來想成為一個軟件結構師嗎?)
Things that are difficult to refactor in (很難重構的東西)
So is Design Dead? (所以,設計死了嗎?)
Acknowledgements (致謝)
Revision History (修訂的記錄)
Extreme Programming (XP) 挑戰很多軟件開發常見的假設。其中最受爭議的就是極力排斥 up-front design,而支持一種比較屬於演進的方式。批評者說這是退回到了 "code and fix" 的開發方式,頂多只能算是一般急就章的程序設計罷了。支持者也常看到 XP 對於設計方法 (如 UML)、principle(設計准則)、patterns 等的排斥。別擔心,仔細注意你的程序代碼,你就會看到好的 design 浮現出來。
我發現自己正陷於這個爭論當中。我的工作著重在圖形化設計語言 - UML 以及 patterns,事實上我也寫過 UML 和 patterns 的書。我如此的擁抱 XP 是否表示我放棄了這些理論,或是將這些反漸進式 (counter-revolutionary) 的概念從腦中清除了?
嗯... 我不想讓你的心懸蕩在這兩種情境中。簡單的說並不是;接下來的文章就讓我來詳細說明。
Planned and Evolutionary Design
我將在這篇文章中說明軟件開發的兩種設計方式是如何完成的。或許最常見的是演進式設計。它的本質是系統的設計隨著軟件開發的過程增長。設計 (design) 是撰寫程序代碼過程的一部份,隨著程序代碼的發展,設計也跟著調整。
在常見的使用中,演進式設計實在是徹底的失敗。設計的結果其實是一堆為了某些特殊條件而巧妙安排的決定所組成,每個條件都會讓程序代碼更難修改。從很多方面來看,你可能會批評這樣根本就沒有設計可言,無疑地這樣的方式常會導致很差勁的設計。根據Kent的陳述,所謂的設計 (design) 是要能夠讓你可以長期很簡單地修改軟件。當設計 (design) 不如預期時,你應該能夠做有效的更改。一段時間之後,設計變得越來越糟,你也體會到這個軟件混亂的程度。這樣的情形不僅使得軟件本身難以修改,也容易產生難以追蹤和徹底解決的 bug。隨著計畫的進行,bug 的數量呈指數地成長而必須花更多成本去解決,這就是 "code and fix" 的惡夢。
Planned Design 的做法正好相反,並且含有取自其它工程的概念。如果你打算做一間狗屋,你只需要找齊木料以及在心中有一個大略的形象。但是如果你想要建一棟摩天大樓,照同樣的做法,恐怕還不到一半的高度大樓就垮了。於是你先在一間像我太太在波士頓市區那樣的辦公室裡完成工程圖。她在設計圖中確定所有的細節,一部份使用數學分析,但是大部分都是使用建築規范。所謂的建築規范就是根據成功的經驗 (有些是數學分析) 制定出如何設計結構體的法則。當設計圖完成,她們公司就可以將設計圖交給另一個施工的公司按圖施工。
Planned Design 將同樣的方式應用在軟件開發。Designer 先定出重要的部份,程序代碼不是由他們來撰寫,因為軟件並不是他們 "建造[譯注3]" 的,他們只負責設計。所以 designer 可以利用像 UML 這樣的技術,不需要太注重撰寫程序代碼的細節問題,而在一個比較屬於抽象的層次上工作。一旦設計的部份完成了,他們就可以將它交給另一個團隊 (或甚至是另一家公司) 去 "建造"。因為 designer 朝著大方向思考,所以他們能夠避免因為策略方面不斷的更改而導致軟件的失序。Programmer 就可以依循設計好的方向 (如果有遵循設計) 寫出好的系統。
Planned design 方法從七○年代出現,而且很多人都用過它了。在很多方面它比 code and fix 漸進式設計要來的好,但是它也有一些缺點存在。第一個缺點是當你在撰寫程序代碼時,你不可能同時把所有必須處理的問題都想清楚。所以將無可避免的遇到一些讓人對原先設計產生質疑的問題。可是如果 designer 在完成工作之後就轉移到其它項目,那怎麼辦?Programmer 開始遷就設計來寫程序,於是軟件開始趨於混亂。就算找到 designer,花時間整理設計,變更設計圖,然後修改程序代碼。但是必須面臨更短的時程以及更大的壓力來修改問題,又是混亂的開端。
此外,通常還有軟件開發文化方面的問題。Designer 因為專精的技術和豐富的經驗而成為一位 designer。然而,他們忙於從事設計而沒有時間寫程序代碼。但是,開發軟件的工具發展迅速,當你不再撰寫程序代碼時,你不只是錯失了技術潮流所發生的改變,同時也失去了對於那些實際撰寫程序代碼的人的尊敬。
建造者 (builder[譯注3]) 和設計者之間這種微妙的關系在建築界也看得到,只是在軟件界更加凸顯而已。之所以會如此強烈是因為一個關鍵性的差異。在建築界,設計師和工程師的技術有清楚的分野;在軟件界就比較分不清楚了[譯注2]。任何在高度注重 design 的環境工作的 programmer 都必須具備良好的技術,他的能力足夠對 designer 的設計提出質疑,尤其是當 designer 對於新的發展工具或平台越來越不熟悉的狀況下。
[譯注3] 在建築業應該是稱呼 “施工人員” 比較順耳,而在軟件業應該是 “programmer” 比較恰當。但是這兩者都是 “builder”。
現在這些問題也許可以獲得解決。也許我們可以處理人與人之間的互動問題。也許我們可以加強 designer 的技術來處理絕大部份的問題,並且訂出一個依照准則去做就足夠改變設計圖的流程。但是仍然有另外一個問題:變更需求。變更需求是軟件項目中最讓我感到頭痛的問題了。
處理變更需求的方式之一是做有彈性的設計,於是當需求有所更改,你就可以輕易的變更設計。然而,這是需要先見之明去猜測將來你可能會做怎樣的變更。一項預留處理易變性質的設計可能對於將來的需求變更有所幫助,但是對於意外的變化卻沒有幫助 (甚至有害)。所以你必須對於需求有足夠的了解以隔離易變的部份。照我的觀察,這是非常困難的。
部份有關需求的問題應該歸咎於對需求的了解不夠清楚,所以有人專注於研究需求處理,希望得到適切的需求以避免後來對設計的修改。但是即使朝這個方向去做一樣無法對症下藥。很多無法預料的變更起因於瞬息萬變的商場,你只有更加小心處理需求問題來應付無法避免的情況。
這麼說來,planned design 聽起來像是不可能的任務。這種做法當然是一種很大的挑戰。但是,跟演進式設計 (evolutionary design) 普遍以 code and fix 方式實作比較起來,我不覺得 planned design 會比較差。事實上,我也比較喜歡 planned design。因為我了解 planned design 的缺點,而且正在尋找更好的方法。
The Enabling Practices of XP
XP 因為許多原因而備受爭議,其中之一就是它主張演進式設計 (evolutionary design) 而不是 planned design。我們也知道,演進式設計可能因為特定的設計或是軟件開發趨於混亂而行不通。
想了解這些爭論的核心,就是軟件研發異動曲線。曲線的變化說明,隨著項目的進行,變更所需要的成本呈現指數的增加。這樣的曲線常以一句話來表示:在分析階段花一塊錢所作的變更,發行之後要花數千元來補救。諷刺的是大部分的計畫仍然沒有分析過程而以非標准的方式進行,但是這種成本上的指數關系還是存在著。這種指數曲線意味著演進式設計可能行不通,它同時也說明著為什麼 planned design 要小心翼翼地規劃,因為任何的錯誤還是會面對同樣的問題。
XP 的基本假設是它可以將這種指數曲線拉平,這樣演進式設計就行得通了。XP 使曲線更平緩並能運用這種優勢。這是因為 XP 實作技巧之間的耦合效果:換句話說,不使用那些能夠拉平軟件開發曲線的實作技巧來工作,這條曲線也不會趨向平緩。這也是爭論的來源,因為評論家不了解這其間的關系。通常這些批評是根據評論家自身的經驗,他們並沒有實行那些有效的實作技巧,當他們看到結果不如預期,對於 XP 的印象也就是這樣了。
這些有效的實作技巧有幾個部份,主要是 Testing 和 Continuous Integration。如果沒有 testing 提供保障,其它的 XP 實作技巧都不可行。Continuous Integration 可以保持團隊成員信息同步,所以當你有改變的部份,不必擔心與其它成員資料整合會有問題。同時運用這些實作技巧能夠大大影響開發曲線。這讓我再次想起在 ThoughtWorks 導入 testing 和 continuous integration 之後,明顯的改善了研發成果。改善的程度好到令人懷疑是不是像 XP 所主張的,必須要用到所有的實作技巧才能大幅改善效率。[譯注4]
[譯注4] 關於這一點,譯者也提供一個實際的經驗,也就是翻譯這篇文章的過程。我和一位朋友一起翻譯這篇文章,而且因為都先看過 XP Distilled 一文,所以決定采用 XP 12 個有效的實務技巧其中的 Continuous Integration、Pair Programming、Small Releases、Refactoring、Coding Standards、Collective Code Ownership 等等技巧。因為 XP Distilled 一文的經驗我們首先對於部份翻譯名詞提出彼此的統一版本,這就是一種 Coding Standards。我們同時都對這篇文章進行翻譯,不同的人進行同一項工作進度就不會相同,每翻譯幾個段落就將進度寄給對方,這就是 Small Releases。當我收到朋友寄來的部份,我就對照兩方的差異將文章針對譯意的正確性和辭句的通暢做初步的整理和修正,這就是 Continuous Integration。我整理過的部份再寄回給朋友進行檢查,如果發現不妥的部份,就進行討論或修改,這就是 Refactoring。從頭到尾的過程中,我們都收到彼此的翻譯版本,也擁有整理過的版本,對於每個部份也都清楚了解,符合 Collective Code Ownership 的精神。最後,因為我們剛好是兩個人,不巧又可以冠上另一個技巧Pair Programming。所以,我們使用了 XP 一半的方法,對於工作上有幫助的方法。但並不是非得每一項技巧都利用到。我想,更重要的一點是,XP 並不是只能用在軟件研發,我們的翻譯過程不也借來用了!
Refactoring 具有類似的成效。那些曾經采用 XP 建議的原則來對程序代碼進行refactoring 的人發現,這麼做要比無章法或是特殊方式的 restructuring 明顯的更有效率。那也曾經是 Kent 指導我適當的 refactor 得到的難忘經驗,也因為這麼一次巨大的轉變促使我以這個主題寫了一本書。
Jim Highsmith 寫了一篇很棒的文章 "summary of XP",他把 planned design 和 refactoring 放在天秤的兩端。大部份傳統的做法假設構想不變,所以 planned design 占優勢。而當你的成本越來越不允許變更,你就越傾向於采用 refactoring。Planned design 並不是完全消失,只是將這兩種做法互相搭配運用取得平衡。對我來說,在設計進行 refactoring 之前,總覺得這個設計不夠健全。
Continuous integration、testing 和 refactoring 這些有效的實作方法讓 evolutionary design 看似很有道理。但是我們尚未找出其間的平衡點。我相信,不論外界對 XP 存有什麼印象,XP 不僅僅是 testing、coding 和 refactoring。在 coding 之前還有 design 的必要。部份的 design 在 coding 之前准備,大部份的 design 則發生在實作每一項詳列的功能之前。總之,在 up-front design 和 refactoring 之間可以找到新的平衡。
The value of Simplicity
XP 大聲疾呼的兩個口號是 "Do The Simplest Thing that Could Possibly Work"(只做最簡單可以正常運作的設計) 和 "You Aren't Going to Need It"(就是 YAGNI - 你將不會需要它)。兩項都是XP實務中簡單設計的表現形式。
YAGNI 一詞時常被討論,它的意思是現在不要為了將來可能用到的功能加入任何程序代碼。表面上聽起來好象很簡單,問題則出在像 framework、重用組件、和彈性化設計,這些東西本來就很復雜。你事先付出額外的成本去打造它們,希望稍後將這些花費都賺回來。這個事先彈性設計的想法被認為是軟件設計有效率的關鍵部份。
但XP的建議是,在處理第一個問題時不要因為可能需要某項功能,就建造出彈性的組件組及框架出來。讓整體結構隨著需要成長。假如我今天想要一個可以處理加法但是不用乘法的 Money 類別,我就只在 Money 類別中建造加法的功能。就算我確定下一個階段也需要乘法的運算,而且我知道很簡單,也花不了多少時間,我還是會留到下一階段再去做它。
其中一個理由是效益。如果我要花時間在明天才需要的功能,那就表示我沒有將精神放在這個階段應該完成的事情上。發表計畫詳列目前要完成的事項,現在做以後才需要的事情違背開發人員和顧客之間的協議。這種做法有讓現階段的目標無法達成的可能。而且這個階段的 stroies[譯注5] 是否具有風險,或是需不需要做額外的工作,都是由顧客來決定的 - 還是可能不包括乘法功能。
[譯注5] Story 是一種類似 use case 的描述。
這種經濟效益上的限制是因為我們有可能出錯。就算是我們已經確定這個功能應該如何運作,都有可能出錯 - 尤其是這時候我們還沒有取得詳細需求。提前做一件錯誤的事情比提前做一件對的事情更浪費時間。而且XP專家們通常相信我們比較有可能會做錯而不是做對(我心有戚戚)。
第二個支持 simple design 的理由是復雜的設計違反光線行進的原理。復雜的設計比簡單的設計還要令人難懂。所以隨著漸增的系統復雜度,更加難以對系統做任何修改。如此,若系統必須加入更復雜的設計時,成本勢必增加。
現在很多人發現這樣的建議是無意義的,其實他們那樣想是對的。因為你所想象一般的研發並沒有被 XP 有效的技巧所取代。然而,當規劃式設計和漸進式設計之間的平衡點有了變化 (也只有當這樣的變化發生時),YAGNI 就會變成好的技巧。
所以結論是,除非到了往後的階段有所需要,否則你不會浪費精神去增加新的功能。即使不會多花成本,你也不會這樣做,因為就算現在加入這些功能並不增加成本,但是卻會增加將來做修改時的成本。總之,你可以在套用 XP 時明智的遵守這樣的方法,或是采取一種能降低成本的類似的方法。
What on Earth is Simplicity Anyway
因此,我們希望程序代碼能夠越簡單越好,這聽起來沒什麼好爭論的,畢竟有誰想要復雜呢?但問題來了,究竟 "什麼才叫簡單呢?"
在 XPE 一書中,Kent 對簡單系統訂了四個評量標准,依序是 (最重要排最前面):
通過所有測試。
呈現所有的意圖。
避免重復。
最少數量的類別或方法。
通過所有測試是一項很普通的評量標准,避免重復也很明確,盡管有些研發人員需要別人的指點才能做到。比較麻煩的是 "呈現所有的意圖"這一項,這到底指的是什麼呢?
這句話的本意就是簡單明了的程序代碼。XP 對程序代碼的易讀性有很高的標准。雖然在 XP 當中,"巧妙的程序代碼 (clever code)" 這個字眼經常被濫用,不過意圖清楚的程序代碼,對其他人來說真的是一種巧妙。Josh Kerievsky 在 XP 2000 論文中舉了一個很好的例子,檢視在 XP 領域可能是大家最熟知的 JUnit 的程序代碼。JUnit 使用 decorators 在 test cases 中加入非必要的功能,像是同步機制及批次設定等,將這些程序代碼抽出成為 decorator,的確讓一般的程序代碼看起來清楚許多。
但是你必須扪心自問,這樣做之後的程序代碼夠簡單嗎?我覺得是,因為我了解 Decorator 這個 patterns。但是對於不了解的人來說還是相當復雜的。類似的情況,JUnit 使用 pluggable method,一種大部分的人剛開始接觸時都不會覺得簡單的技巧。所以,也許我們可以說 JUnit 對有經驗的人來說是比較簡單的,新手反而會覺得它很復雜。
XP 的 "Once and Only Once" 以及 Pragmatic Programmer(書名) 的 DRY(Don't Repeat Yourself) 都專注在去除重復的程序代碼。這些良好的建議都有很顯著而且驚人的效果。只要依照這個方式,項目就可以一路順利的運作。但是它也不能解決所有問題,簡單化還是不容易達成。
最近我參與一個可能是過度設計的項目,系統經過 refactor 之後去除部份彈性的設計。但是就像其中一位開發者所說的 "重構過度設計的系統要比重構沒有設計的要來的容易多了" 做一個比你所需要簡單一點的設計是最好的,但是,稍微復雜一點點也不是什麼嚴重的事情。
我聽過最好的建議來自 Bob 大叔 (Robert Martin)。他的建議是不要太在意什麼是最簡單的設計。畢竟後來你可以,應該,也會再重構。願意在最後重構,比知道如何做簡單的設計重要得多。
Does Refactoring Violate YAGNI?
這個主題最近出現在 XP 討論區上,當我們審視設計在 XP 扮演的角色時,我覺得很值得提出來討論。
基本上這個問題起因於重構需要耗費時間卻沒有增加新的功能。而 YAGNI 的觀點是假設你為了眼前的需要做設計而不是未來,這樣算是互相抵觸嗎?
YAGNI 的觀點是不要增加一些現階段不需要的復雜功能,這也是簡單設計這項技巧的部份精神。重構也是為了滿足盡可能保持系統的簡單性這個需要,所以當你覺得可以讓系統變得更簡單的時候,就進行重構。
簡單設計不但利用了 XP 的實務技巧,本身也是其中一項有用的實務技巧。唯有伴隨著測試,持續整合,及重構的運用,才能有效地做出簡單設計。同時,讓研發異動曲線保持平緩的基礎也就是保持設計的簡單。任何不必要的復雜都會讓系統變得難於調整,除非這個復雜性是你為了所預測的彈性而加入的。不過,人們的預測通常都不太准確,所以最好還是努力地保持簡單性。
不管怎樣,人們不太可能第一次就能夠獲得最簡單的東西,因此你需要重構來幫助你更接近這個目標。
Patterns and XP
JUnit 的例子讓我不得不想到 patterns。XP 和 patterns 之間的關系很微妙,也常常被問起。Joshua Kerievsky 認為 patterns 在 XP 被過分輕視,而且他所提出的理由也相當令人信服,我不想再重提。不過值得一提的是,很多人都認為 patterns 似乎與 XP 是有沖突的。
爭論的本質在於 patterns 常被過度濫用。世上有太多傳奇性的 programmer,第一次讀到四人幫以 32 行程序代碼闡述 16 種 patterns 這樣的事情還記憶猶新[譯注6]。我還記得有一晚與 Kent 喝著醇酒一起討論一篇文章 "Not Design patterns: 23 cheap tricks (不要用設計模式-23 個簡單的訣竅)"。我們認為那不過是以 if 條件式來取代 strategy 這個 pattern 罷了。這樣的笑話有個重點,patterns 被濫用了。但並不表示 patterns 是不足取的,問題在於你要怎麼運用它。
[譯注6] 按照原文的意思就是以 32 行的程序代碼而能夠表現出 16 種 patterns!這樣的傳奇性功力實在不是經驗及見識淺薄的譯者所能想象,所以我們只能按照原文翻譯,而無法對這句話提出左證。如果讀者見過作者提到的例子,很感謝您提供相關資料給我。
其中一項論點是簡單設計的力量自然會將項目導向 patterns。很多重構的例子明確地這麼做,或者甚至不用重構,你只要遵從簡單設計的規則就會發現 patterns,即使你還不知道 patterns 是什麼。這樣的說法也許是真的,不過它真的是最好的方式嗎?當然如果你先對於 patterns 有個大略的了解,或者手邊有一本書可以參考,會比自己發明新的 patterns 要好些。當我覺得一個 pattern 快浮現的時候,我必定會去翻翻 GOF 的書。對我來說,有效的設計告訴我們 pattern 值得付出代價去學習-那就是它特有的技術。同樣地就像 Joshua 所建議的,我們需要更熟悉於如何逐步地運用 patterns。就這一點而言,XP 只是與一般使用 patterns 的方式不同而已,並沒有抹煞它的價值。
但是從討論區一些文章看來,我覺得很多人明顯地認為 XP 並不鼓勵使用 patterns,盡管 XP 大部分的提倡者也都是之前 patterns 運動的領導者。因為他們看到了不同於 patterns 的觀點嗎?或是他們已經將 patterns 融入思考而不必再去理解它?我不知道其它人的答案是什麼,但是對我來說,patterns 仍然是非常重要的。XP 也許是開發的一種流程,但 patterns 可是設計知識的骨干,不管是哪種流程這些知識都是很有用的。不同的流程使用 patterns 的方式也就不同,XP 強調等到需要時才使用 patterns 以及透過簡單的實作逐步導入 patterns。所以 patterns 仍然是一種必須獲得的關鍵知識。
我對於采用 XP 的人使用 patterns 的建議:
花點時間學習 patterns。
留意使用 patterns 的時機 (但是別太早)。
留意如何先以最簡單的方式使用 patterns,然後再慢慢增加復雜度。
如果用了一種 pattern 卻覺得沒有多大幫助-不用怕,再次把它去掉。
我認為XP應該要更加強調學習 patterns。我不確定它要怎麼和 XP 的實務技巧搭配,不過相信 Kent 會想出辦法來的。
Growing an Architecture
軟件架構是指什麼呢?對我來說,架構這個字眼代表系統核心組件的概念,也就是難以改變的部份,剩下的都必須建造在這基礎上。
那麼當你使用演進式設計時,架構又扮演著什麼樣的角色呢?XP 的批評者再一次地聲稱 XP 忽視架構,因為 XP 使用的方法是盡快地寫程序,然後相信重構會解決所有設計的問題。很有趣地,他們說得沒錯,這有可能是 XP 的缺點。無疑地,最積極的 XP 專家-像 Kent Beck, Ron Jeffries, 及 Bob Martin-盡其所能地避免預先結構性的設計。在你知道真的要用到數據庫之前,不要加入數據庫,先用檔案來代替,在之後的階段再用重構加入數據庫。
我常被認為是一個膽小的 XP 專家,這點我不同意。我認為一個概括性的初始架構有它的用處在。像是一開始要怎麼將應用分層,如何與數據庫互動 (如果你需要的話),要使用哪種方式去處理網站服務器。
基本上,我認為這些就是近年來我們所研究的 patterns。尤其當你對 patterns 的認識越深,你就會越熟悉要怎麼去善用它們。不過,關鍵性的差異是在於這些初期架構的決定是可以更改的,只要團隊認為他們早期的判斷有誤時,就應該要有勇氣去修正它們。有人跟我講了一個項目的故事,就在項目快要發表時,決定了不再需要 EJB,並且要將它們從系統中移除。這是一個相當大規模的重構,不過最後還是完成了。這些有效的實務技巧不僅讓事情變得可能,而且很值得去做。
如果以不同的方式來做這件事呢?如果你決定不采用 EJB,將來會難以加入嗎?你是否要在試過各種方式卻發現依然欠缺什麼,然後才使用 EJB?這是一個牽涉很多因素的問題。不使用復雜的組件當然可以增加系統的簡單度,而且可以讓事情進展比較快,但有時候從系統中抽掉某個部份會比加入它要容易多了。
所以我建議從評估架構可能的樣子開始。假如你看到將會有多個使用者使用到大量的資料,一開始就直接使用數據庫。若你看到很復雜的商業邏輯,就套用 domain model。你會懷疑是否偏離簡單的特性,這當然不是 YAGNI 的精神。所以你要有所准備,在發現所使用的結構沒有幫助時盡快簡化你的結構。
UML and XP
在我投身於 XP 領域之後,由於我與 UML 的關系讓我遇到一個揮之不去最大的問題:這兩者不是不兼容嗎?
當然有些不兼容。XP 顯然不重視 diagram。雖然台面上大家對 "好用就用" 有共識,但是實際上卻是 "實際上采用 XP 的人不畫藍圖"。這種印象因為如 Kent 這些人不習慣作圖的現象而強化了。事實上我也從來沒看過 Kent 主動使用固定的標記法畫下軟件藍圖。
我覺得這種情形來自兩個因素,其一是有人覺得軟件藍圖有用,而有人不覺得有用。難就難在覺得藍圖有用的人不是真正必須動手做的人,而必須動手做的人卻不覺得有其必要性。事實上我們應該接受有人喜歡用,而有人不喜歡用。
另一種情形是軟件藍圖常引人進入繁重的流程中,這些流程耗時費力卻不見得有用,甚至還會產生壞處。我認為應該教導人們如何使用藍圖卻不落入這樣的陷阱,而不是像那些提倡者僅僅消極的說 "必要時才用"。
所以,我對於有效使用藍圖的建議是:
首先別忘了你畫這些圖的目的,主要的價值在於溝通。有效的溝通意味著選擇重要的部份而忽略不重要的部份。這樣的選擇也是有效運用 UML 的關鍵。不必把全部的 class 都畫出來,畫出重要的就好。對於每個 class 也只顯示關鍵的 attribute 和 operation,而不是全部顯示出來。也不要為所有的 use case 和步驟畫循序圖... 除非你已經有完整的想象。有一個使用藍圖的通病就是人們通常希望詳細完整的把圖表現出來。其實程序代碼就是提供完整信息的最佳來源,同時程序代碼本身也是保持信息同步最簡單的方式。因為圖形的巨細靡遺就是一目了然的敵人。
藍圖的用途是在開始撰寫程序代碼之前探討設計內容。印象中總是覺得這樣的動作在 XP 是不合法的,但並不是這樣。很多人都說如果你遇到棘手的問題,就值得先做些設計。但是當你進行設計時:
保持簡短。
不要做得太詳細(只挑重要的做)。
把結果當作是草圖,而不是定案。
最後一點值得深入探討。當你做預先式設計,無可避免的會發現一些錯誤,而且是在撰寫程序代碼的時候才發現。如果你適時變更設計,它就不是問題。麻煩的是如果你認定設計已經定案,沒有從 coding 過程學到經驗而跟著先前的設計將錯就錯。
變更設計不代表一定要更改藍圖。畫這些藍圖來幫助你了解設計,然後就把圖扔開,這麼做是非常合理的。這些圖能夠幫上忙就有它的價值了。它們不必永遠存在,最有用的 UML 圖形也不會是收藏品。
不少實行 XP 的人使用 CRC 卡,這與 UML 並不沖突。我常常交互運用 CRC 卡和 UML,我也總是依照手上的工作選擇最有用的技巧。UML 圖形的另一個用途是持續修訂的文件。它一般的形式,就是在 case tool 中看到的模型。最初的想法是留著這樣的資料有助於建構系統。事實上卻常常沒什麼用。
保持圖形的更新太花時間,最後常無法與程序代碼同步。
它們隱含在 CASE tool 或 thick binder,讓人忽略它。
所以要希望這種持續修訂的文件有用,就從這些看到的問題下手:
只用一些改起來不至於讓人覺得痛苦的圖。
把圖放在顯眼的地方。我喜歡畫在牆上,鼓勵大家一起動手修改。
檢討這些圖是不是有人在用,沒用的就擦掉。
使用 UML 的最後一個問題是文件的交接,像是不同團隊的接手。XP 的想法是文件就像說故事,所以文件的價值由顧客來決定。於是 UML 又派上用場,所提供的圖形可以幫助溝通。別忘了程序代碼本身就蘊含了所有詳細的信息,圖形的作用只是提供概觀以及標示重要的部份。
On Metaphor
好吧,我也許該坦承 - 我一直沒有抓住 metaphor 的精神。它有用,而且在 C3 項目中運用得很好,但是並不表示我知道怎麼用它,更不用說要解釋怎麼用了。
XP 實務技巧中的 Metaphor 是建立在 Ward Cunningham's 為系統命名的做法上。重點是想出一個眾所周知的詞匯,以這樣一個字來比喻整個范疇。這個代表系統的名字會套用在 class 和 method 的命名上。
我以不同領域的觀念性模型,利用 UML 和它的前身與領域專家一起建立了一個命名系統。我發現你必須很小心,你要保持最精簡的注釋,而且要當心別讓技術性的問題不知不覺的影響這個模型。但是一旦你完成這個工作,你就可以為各種領域建立一組詞匯,這些詞匯是大家都能了解並且可用來與研發人員溝通的。這種模型無法與 class 設計完美的吻合,但是足夠給整個領域一個通用的代名詞。
目前我找不到任何理由說明為何這樣的一個字匯無法成為一個比喻,就像 C3 這個成功的例子;我也不覺得以系統為主找到在該專業領域的一個詞匯有什麼壞處。同時我也不會放棄可以運作自如的為系統命名的技巧。
人們常批評 XP 乃是基於覺得一個系統實在是至少需要一個大概的設計。XP 專家則以 "就是 metaphor 啊!" 來響應。但是我還是沒有看到一個對於 metaphor 令人信服的解釋。這是 XP 的缺憾,必須由 XP 專家來理出頭緒。
Do you wanna be an Architect when you grow up?
近幾年來 "software architect (軟件設計師)" 越來越熱門,這是一個就我個人而言難以接受的名詞。我太太是結構工程師,工程師和建築師之間的關系是... 有趣的。我最喜歡的一句話是:建築師對三種 B 是好的,燈泡、灌木叢、和鳥。因為建築師畫出這些美麗的圖畫,但卻要工程師保證能做出來。結論是我避免 software architect 一詞,畢竟如果連我的太太都不能尊重我的專業,我又怎麼能對其他人有所期望呢?
對軟件來說,architect 一詞可以代表很多事情。(軟件界很多詞都可以代表很多事。) 這通常符合一句話:我不僅是一個程序員,我還是一個設計師[譯注2]。還可以進一步解譯成:我現在是一個設計師 - 我對於完成所有程序來說太重要了。然後這個問題就變成,當你要展現技術領導的時候,你是不是該把自己與煩瑣的程序撰寫分清楚?
這個問題引起眾多的不滿。我看到人們對於再也無法擔任設計角色這樣的想法感到生氣。我最常聽到:在 XP 沒有設計師的揮灑空間。
就設計本身的角色來說,我不覺得 XP 不重視經驗或好的設計。事實上多位 XP 的提倡者 - Kent Back、Bob Martin、當然還有 Ward Cunningham - 都是我從而學習設計的對象。然而這也代表著他們的角色從大家既有的印象中開始轉變成為技術領導者。
我將以一位 ThoughtWorks 的技術領導者 Dave Rice 為例。Dave 參與了很多個研發周期,並且非正式的指導一個 50 人的項目。他擔任指導的角色意味著要花很長的時間與程序員為伍。當程序員需要幫助,他就介入,否則就留意著看誰需要協助。他的座位有一個明顯的特征,擔任一位長期的思考工作者,他可以在任何形式的辦公環境適應良好。他曾經與發行部經理 Cara 共享辦公室一段時間。而在最後幾個月,他更是搬到工程師們工作的開放式空間 (就像 XP 小組喜歡的開放式 "戰斗空間"javascript: if (this.width > screen.width / 2) { this.width = screen.width / 2; }}" src="http://www.itjc.cn/A-A-B/Image/2005/09/13/200509132228247_1.gif" onclick="function onclick(event) { window.open("/images/wink.gif", "_blank");}" />。這麼做對他很重要,他可以知道事情的進展,並適時伸出援手。
知道 XP 的人就能夠了解我描述的是 XP "教練" 的清楚角色。的確,在 XP 玩的文字游戲中提到領導技術就是在描繪 "教練" 這個角色。其意義是很清楚的:在 XP 技術的領導特質是透過教導程序員和幫助他們做決定而呈現。這是一種需要良好人際管理和技術並重的技巧。Jack Bolles 在 XP2000 說:孤單的大師有點機會了,合作和教導是成功的關鍵。
在研討會的晚餐會上,我和 Dave 在談話時對 XP 有了些對立的意見。當我們討論到以前的經驗,我們的方法有相當的類似。我們都偏好 adaptive,iterative development,也認為測試是重要的。所以我們都對他反對的立場感到疑惑。然而他提到 "最後我要的是程序員照著設計忙於重構"。事情一下子明朗起來。後來 Dave 又對我說 "如果我不信任我的程序員,我何必要用他們呢?",觀念上的隔閡就更加清楚了。在 XP 裡頭,有經驗的研發人員所能做的最重要的一件事就是盡量將所有技術傳繼給新手。不同於一個決定所有重要事情的建築師,你有一個能夠教導研發人員如何做重大決定的教練。就像 Ward Cunningham 指出,這麼做不只是增進了新手的能力,對項目的好處更大於一個孤立無援的超人所能做的。[ 譯注7]
[譯注7] 這一點也是譯者對於台灣軟件環境非常憂心的問題,大家都能體會中國人的一種不知道能不能說是重大缺點的民族習性 “藏私”。我在不同的機會從文章中以及朋友的口中聽到一種想法,要提升團隊的整理效率,一種最簡單的方法就是將技術落後的成員教育提升到與技術領先的成員相同的水平。這樣的想法實在是一種非常有用而且深刻的體認,更是一種感歎。在台灣能夠在口頭上同意這種做法,而實際上更能夠進而實踐這種精神的人有多少??有些人都擔心自己的技術被人窺透之後,個人地位恐將遭到取代,更遭的是可能有老板真的會 “狡兔死走狗烹” 的在搾取經驗的傳承之後,將有貢獻卻成本高的成員以各種方式逼走,這實在是讓人憂心又灰心的現象啊!譯者相信,再困難的技術只要是你能了解,就已經存在了競爭對手,如果不能將身邊成員的技術水平同時迅速提升,只有等著被快速超越,更可能被淘汰。譯者也實踐著 XP 這方面的精神,雖然譯者所擁有的技術知識非常粗淺,但是只要同事朋友需要我的協助,我總是知無不言,言無不盡。譯者要向流星許願,希望能夠找到越來越多的同類。
Things that are difficult to refactor in
我們能用 refactoring 來處理所有設計方面的決定嗎?或者,有些問題太普遍而且難以在將來加入設計中?此時,XP 的正統做法是所有需求都可以輕易的在需要的時候增加,所以 YAGNI 總是能夠適用。我猜是不是有例外?有一個不錯的,被討論到的例子是軟件的國際化。這是不是一種現在應該立即進行,否則以後再加入時會覺得痛苦的事情?
我能輕易的想象一些事情就是這種情形。事實上我們仍然掌握太少的信息。如果你必須陸續加入一些功能,如國際化,而你知道那需要多少工夫。你比較不容易意識到在你真正需要他之前,你要花多少時間加入它,並且要長時間的維護它。你也比較不容易察覺到也許你可能做錯了,所以到頭來還是需要做些 refactoring。
有一部份能夠為 YAGNI 辯護的理由是有些預先做的功能可能最後並不需要,或者它並不如預期的結果。不做這些所省下的力氣比用 refactoring 來更改成為符合需要所用的力氣要少。
另外一個要想的問題是你是否真的知道怎麼做。如果你有很多次做軟件國際化的經驗,你會知道該用什麼模式來作。那樣的情形下你應該會把它作好。這時你所加入的預留的結構可能會比你頭一次處理這種問題要好。所以我的想法是,如果你知道怎麼做,你就要考慮現在做和將來做,兩種情形之間不同的成本。反過來說,如果你沒有處理過那樣的問題,不僅是你無法正確判斷需要的成本,你也比較不可能把事情作好。這種情形,你就要選擇將來再做。如果你還是執意做了,而且嘗到苦果,可能會比不做的情況更糟。當你的組員更有經驗,你對相關領域有更多認識,你對需求也會更了解。通常到這時你回頭看才會發現事情有多簡單。提早加入的設計比你想象中要難多了。
這個問題也跟 stories 的順序密切相關。在 Planning XP 一書中,Kent 和我公開的指出我們的歧見。Kent 偏向於只讓商業價值這一個因素影響 stories 的順序。在短暫的意見不合之後 Ron Jeffries 也同意這種想法。我仍保持懷疑。我相信在商業價值和技術風險之間能找到平衡點。基於這樣的理由讓我提早為軟件國際化做准備以降低風險。但是這種做法也只有當第一階段發行就需要將軟件國際化才能成立。盡快達到一個階段的發行是非常重要的。任何原來不需要而後來必須增加的復雜性都值得在第一階段發行之後才開始。發行之後運作中的程序有強大的力量,它抓住顧客的注意,增加信任感並且是一個學習的好機會。就算是在初次發行之後會有更多的事情要做,還是要盡所有努力將第一階段日期往前推。
So is Design Dead?
沒什麼原因,只是設計的本質已經改變。XP 的設計追求以下的技巧:
持續保持清爽的程序代碼,越簡單越好。
重構的技巧,所以當你覺得必要的時候都可以有信心的動手。
具有 patterns 的知識:不只是照它的解法,更要感覺何時可以應用,或是如何導入 patterns。
知道如何將設計說給必要的人了解[譯注8],用程序代碼、或是圖形、或上述所有的工具:交談。
[譯注8] 有效的溝通也是譯者常遭遇的問題之一,有些人無法以不同的方式為事情下批注,有些人對於門外漢還是滿口專業術語,有些人不能觀察顧慮到聽者是不是已經了解所闡述的精髓。這些都不是有效的溝通。我們似乎還不夠重視人際溝通這門學問,如何以對方能懂的方式說給對方聽,真是不容易呢!譯者只是發出感歎,卻也亟需要培養溝通能力,才不會讓共事的同事搖頭。
以上挑出來的技巧看來都挺嚇人,但是要成為一個優秀的設計師本來就很難。XP 也不是要讓它變得簡單,至少我就不覺得。但是我想 XP 讓我們對有效率的設計有全新的看法,因為它讓漸進式設計聽起來是可行的方式。而且我也很支持演進 - 否則誰知道我會變成什麼呢?
Acknowledgements[譯注9]
[譯注9] 譯者之一的我 (Daimler) 也要搭個順風車,感謝朋友 (S.H.Chen) 在百忙之中抽空與我完成這篇文章的翻譯,這位朋友在軟件工程方面的知識給我的幫助非常關鍵。翻譯本身就存在不少爭議,應該按照句子逐字翻譯,還是要考慮依據本國文字的用語習慣做適當的調整,以求字句的通順,真的讓人難以取捨抉擇。我 (Daimler) 是比較傾向於在不扭曲原著作文意的原則下,對句子做適當的重組和調整。但是這個動作本身就存在著非常大的危險,我對於原文是否有足夠的認識?我對於本國文字的造詣是否及格?希望這篇文章對於國內讀者能有所幫助,至少是效率上的幫助。更期盼來自各方的指教。
過去這些年我從很多好朋友身上偷學到不少好的想法,很多都已經記不起來。但是我記得從 Joshua Kerievski 那裡偷到的好東西。我也記得 Fred George 和 Ron Jeffries 給我很好的建議。我當然也不能忘記 Ward 和 Kent 不斷有好的想法。
我也感謝曾經提出問題和指出打字錯誤的朋友。同時感謝 Craig Jones 提醒我好幾個漏掉的 “a”。
Revision History
以下是這篇文章重大的修改記錄:
2001 年 2 月:對 growing an architecture、the role of an architect、和 where things that are difficult to add with refactoring 等段落做修改。
2000 年 7 月:原始文件在 XP 2000 發表,並刊載於 MartinFowler.com 網頁。