設計已死?(Ai92重譯版)

設計已死?

 

Martin Fowler   Ai92

英文原文版權由Martin Fowler擁有
Original text is copyrighted by Martin Fowler

Martin Fowler
Chief Scientist, ThoughtWorks

 

聲明:任何人都可以在任何地方隨意轉載本文,但是在轉載時請保持本文完整性,請不要在轉載的時候做任何改動或增刪。

 

*****************************************************************

對很多粗略接觸到極限編程(Extreme Programming)的人來說,XP似乎宣告了軟件設計的死刑。不僅有很多的設計被嘲笑爲臃腫的預先設計(Big Up Front Design)”,就連很多設計技術,像UML、靈活的程序架構(framework),甚至連模式(pattern)都不再受到重視甚至被徹底的忽略了。事實上,XP包含了很多的設計理念,但是它與現有的軟件過程有着不同的運作方式。XP通過許多實踐賦予演進式設計(evolutionary design)嶄新的風貌,讓演進成爲一種可行的設計方法(viable design strategy)。但是它也帶來了新的挑戰和技巧,因爲設計者(designer)要去學習如何使設計精簡,如何使用重構來保持設計的簡潔,以及如何使用模式。

 

(這篇文章是我在XP2000研討會發表的演說,它會發表在研討會講義中。)

    經過規劃的設計與演進式的設計(Planned and Evolutionary Design)

    XP實踐(The Enabling Practices of XP)

    簡單的價值(The value of Simplicity)

    究竟什麼是簡單(What on Earth is Simplicity Anyway)

    重構違反了YAGNI嗎?(Does Refactoring Violate YAGNI?)

    模式與XP(Patterns and XP)

    發展架構(Growing an Architecture)

    UMLXP(UML and XP)

    關於隱喻(On Metaphor)

    你想成爲架構師嗎?(Do you wanna be an Architect when you grow up?)

    可逆性(Reversibility)

    設計自律(The Will to Design)

    很難重構的東西(Things that are difficult to refactor in)

    產生設計了嗎?(Is Design Happening?)

這樣來看設計死了嗎?(So is Design Dead?)

    致謝(Acknowledgements)

    修訂記錄(Revision History)

   

XP挑戰了很多在軟件開發中普遍存在的行爲。其中最受爭議的就是反對up-front design,而支持演進的方式。批評者說這是退回到了僅有"code and fix" ,通常被嘲笑爲黑客式的開發方式。而極限編程的支持者也往往被看作排斥設計技術(就像UML)、原則和模式的激進分子。不用擔心什麼設計,只要你傾聽(listen to)你的代碼,好的設計會浮現出來的。

我發現自己陷入了這個爭論的中央。我從事的很多工作都和圖形設計語言(graphical design languages)以及模式(patterns)有關,其中圖形設計語言包括UML以及早於UML的其它圖形設計語言。實際上我曾經寫過關於UML和模式方面的書。我擁抱XP就意味着要放棄我曾經主張的一切,將這些“反革命”的觀點統統從腦子中清除掉?

... 我不想讓你的思緒懸在這個疑問裏。簡單的說答案是否定的。接下來的文章就讓我來詳細說明。

 

經過規劃的設計與演進式的設計(Planned and Evolutionary Design)

 

我將在這篇文章中描述軟件開發中的兩種設計方式。或許最常見的是演進式設計。它的本質是系統設計隨着系統實現逐步增長。設計是編碼過程中的一部分,並且隨着代碼的發展,設計也要跟着調整。

在常見的使用中,演進式設計帶來的是災難。設計的結果其實是一堆特定的戰術策略(ad-hoc tactical decisions)的集合,使得代碼變得更難以修改(alter)。從很多方面來看,你可能會爭辯說這根本沒有設計,當然會導致差勁的設計。像Kent表述的那樣,所謂設計從長遠的觀點來看就是要做到很容易地變動軟件。當設計開始變壞時,你應該能夠做出有效的更改。一段時間之後,設計變得越來越糟,你就體會到了軟件的混亂。這樣的情形不僅使軟件變得本身難以變動,也容易產生難以追蹤和徹底解決的bug。隨着工程的進行,修改不斷出現的bug 所花費的成本呈指數地增長,這就是"code and fix"的噩夢。

Planned Design的做法正好相反,並且吸收了其它工程學中的觀念。如果你打算做一間狗屋,你只需要找些木料並在心中有一個大略的構思就可以了。但是如果你想要建一棟摩天大樓,同樣的做法,恐怕蓋不到一半大樓就垮了。那麼你要先在一間像我太太在波士頓市區那樣的辦公室裏完成工程圖。她在設計圖中確定所有的細節,一部分使用數學分析,但是大部分都是使用建築規範。所謂的建築規範就是根據成功的經驗 (有些是基礎數學) 制定出的如何設計建築的法則。一旦設計完成,她所在的設計公司就將設計圖交給另一個公司按圖施工。

Planned Design 將同樣的方式應用在軟件開發中。設計師預先設計出大部分得細節。設計師們並不負責編寫代碼,他們只負責設計。所以設計師們可以利用像UML這樣的設計技術從編碼的細節中脫離出來,而在一個比較抽象的層面上工作。一旦設計完成了,他們就可以將它交給另一個團隊(甚至是另一家公司)去建造(build)。因爲設計師們在宏觀的層面上考慮問題,所以他們能夠避免因爲策略方面不斷的更改而導致軟件的失序。程序員遵循着設計好的方向,寫出好的系統。

自從Planned design方法從七十年代出現,已經有很多人用過它了。在很多方面它比”code and fix”演進式設計要來的好。但是它也有一些缺點。第一個缺點是當你在編寫代碼時,你不可能把所有必須處理的問題都想清楚。所以將無可避免的遇到一些讓人對原先設計產生質疑的問題。可是如果設計師在完成工作之後就轉移到了其它項目,怎麼辦?程序員開始遷就設計來寫程序,於是軟件開始趨於混亂。就算找到了設計師,花時間整理設計,變更設計圖,然後修改程序代碼。但是必須面臨更短促的時間壓力來修改問題,又是混亂的開端。

此外,Planned design通常還有文化方面的問題。設計師有着熟練的技巧和豐富的經驗。然而,他們忙於從事設計而沒有時間來編寫代碼。可是,軟件開發使用的工具和素材(materials)發生着日新月異的變化。當你不再編寫代碼時,你不僅僅錯失了技術變革帶來的好處,同時也失去了那些編寫代碼的程序員對你的尊敬。

建造者(builder)和設計者之間這種緊張的關係在建築行業也看得到,只是在軟件行業更加凸顯而已。在軟件行業之所以會如此強烈是因爲和建築行業有着一個關鍵性的差異。在建築行業,設計師和工程師的技術有清楚的分別;但是在軟件行業就分不太清楚了。任何在高度注重設計的環境中工作的程序員都必須具備熟練的技術,以便足夠對設計師的設計提出質疑,尤其是當設計師對於新的開發工具或平臺越來越不熟悉的情況下。

這些問題也許可以獲得解決。也許我們可以解決人與人之間的緊張狀態。也許我們可以加強設計師的技術來處理絕大部分的問題,並且制訂出一個修改設計圖的過程準則。但是仍然有另外一個問題:變更需求。變更需求是軟件項目中最讓我感到頭痛的問題之一。

處理變更需求的方式之一是做靈活的設計,以便當需求有所變動時,你就可以輕易的變更設計。然而,這需要你對將要發生的變動有深刻的洞察力。一項預留處理易變性質的設計可能對於可預知的需求變更有所幫助,但是對於無法預知的需求變更卻沒有幫助(甚至有害)。所以你必須對需求有足夠的瞭解以隔離易變的部分,照我觀察,這是非常困難的。

部分有關需求的問題被歸咎於對需求的瞭解不夠清楚。所以很多人開始專注於研究需求工程過程(requirements engineering processes),希望得到更準確的需求以避免後面對設計的修改。但是即使朝這個方向去做一樣無法解決問題。很多無法預知的需求變更起因於業務的變化。這是不能避免的,即使你小心翼翼的使用着需求工程過程。

以上問題使得planned design聽起來像是不可能的實現的。無疑,這是很有挑戰性的。但是我不認爲planned design比使用”code and fix”方式的演進式設計差。甚至,我更喜歡 planned design。因爲我瞭解 planned design 的缺點,並且正在尋找更好的方法。

 

XP實踐(The Enabling Practices of XP)

 

XP因爲許多原因而備受爭議,其中最關鍵的原因就是它主張演進式設計而不是planned design。正如我們知道的,演進式設計可能因爲特定的設計策略(ad hoc design decisions)和照成軟件開發混亂而行不通。

理解這個爭議的關鍵在於軟件變更曲線(software change curve)。變更曲線說明,隨着項目的進行,變更所花費的成本呈現指數的增長。變更曲線按照不同的階段可以表示爲:在分析階段花一塊錢所作的變更,放在編碼階段中則要花費數千元。諷刺的是大部分的工程仍然沒有分析過程而以非正式的方式進行着,但是這種成本上的指數關係還是存在着。變更曲線意味着演進式設計可能行不通,它同時也說明了爲什麼planned design要小心翼翼的進行,因爲在planned design中產生的任何錯誤同樣會導致指數增長。

XP的基礎是建立在將變更曲線拉平,使得演進式設計可行的假設上的。XP將變更曲線拉平而又爲自己所用。XP中一系列相互依賴的實踐來說:你不能僅僅使用那部分以平滑曲線爲前提的XP利用實踐(exploiting practices)而放棄那些建造平滑曲線這個前提的XP啓動實踐(enabling practices) [譯註1]這就是爭論的來源,因爲很多人不瞭解這其間的關係。通常評論家們提出的批評,是基於自身對XP的體驗,但是他們割裂了XP中相互依賴的實踐,結果可想而知,於是對XP的印象也就這樣了。

XP中有許多啓動實踐,其中最重要的是測試(Testing)和持續集成(Continuous Integration)。如果沒有測試提供保障,其它的XP實踐都將變得不可行。持續集成可以保持團隊成員信息同步,這樣當你修改代碼時,不必擔心與其他團隊成員集成會有問題。協同運用這些實踐可以很大的影響變更曲線(change curve)。這讓我再次想起在ThoughtWorks引入測試和持續集成之後,明顯的減輕了開發工作量(development effort)。這的確令人質疑是不是必須像XP所主張的那樣,要用到所有的實踐才能得到大幅改善。

重構(Refactoring)具有類似的作用。那些採用XP建議的原則對程序代碼進行重構的人發現,這麼做要比無章法或是特殊方式的restructuring明顯更有效率。這也曾是Kent指導我進行適當的重構得到的難忘經驗。也因爲這一次巨大的轉變促使我以重構爲主題寫了一本書。

Jim Highsmith寫了一篇很棒的文章《XP概要(summary of XP)》,文中他把planned design和重構模擬作了天秤的兩端。假設你不會隨後變更需求,那麼在大多數傳統方法中planned design佔有優勢。但是當需求發生變更,則重構佔有了優勢,它可以大幅降低變更帶來的成本。當然我們不能完全拋棄planned design,而是採用一種保持“天平”平衡的協作方式。就我個人理解是:我會一次做完所有的設計然後不斷的重構。

持續集成、測試和重構這些啓動實踐讓演進式設計變得可行起來。然而我們尚未找到其間的平衡點。我相信,不論外界對XP存有什麼樣的印象,XP不僅僅是測試、編碼和重構。在編碼之前還是需要進行設計。一些設計要在所有的編碼開始之前進行,而大部分的設計直到迭代中要實現具體功能時才進行。但是預先設計和重構之間要找到新的平衡。

 

簡單的價值(The value of Simplicity)

 

XP大聲疾呼的兩個口號是“考慮能夠工作的最簡單的事情(Do The Simplest Thing that Could Possibly Work)”和“你將不需要它(You Aren't Going to Need It) (就是YAGNI)。它們都表達了XP提倡的實踐——簡單設計(simple design)

YAGNI的意思是現在不要爲了將來可能用到的功能編寫任何代碼。表面上好像很簡單,但是問題出在像框架(framework)、可重用組件和靈活性設計上,這些東西本來就很複雜。你預先付出額外的成本去構建它們,以期望以後的開發中會節省成本。事先構建靈活的預先設計被認爲是高效軟件設計的關鍵部分。

XP的建議是,不要以需要某項功能爲理由來構建靈活的組件及框架出來。讓整體架構隨着需要成長。假如我今天想要一個可以處理加法但是不用乘法的Money類,我就只在Money類中構建加法功能。就算我確定下一次迭代中確實需要乘法運算,而且我知道實現會很簡單。我還是會留到下一次迭代中再去做它。

其中一個理由是經濟效益。如果我花時間在以後才需要的功能上,那就表示我沒有將全部精力放在這次迭代中應該完成的事情上。已經發布的計劃明確指明瞭現在要進行的工作,如果現在做將來才需要做的事情,就違背了開發人員和客戶之間的協議。這種做法使得完成當前迭代過程存在風險。即使完成本次迭代所有素材不存在任何風險,要做哪些額外工作也是由客戶來決定的——還是有不包括乘法功能的可能。

這種經濟效益上的限制是因爲我們有可能出錯。就算是我們已經確定這個功能應該如何運作,都有可能出錯——尤其是在我們還沒有得到詳細需求的時候。提前做一件錯誤的事情比提前做一件對的事情更浪費時間。而且XP專家們通常認爲我們更有可能做錯而不是做對(我心有慼慼)。

第二個支持簡單設計的理由是複雜的設計要比簡單的設計令人難懂。因此隨着系統複雜度的提高,對系統進行任何修改變得越來越難。如此,若系統必須加入更復雜的設計時,成本勢必增加。

現在很多人發現這個建議是無意義的,其實他們這樣想是對的。因爲你所想象的普通開發並沒有採用XP啓動實踐。然而,當planned design和演進式設計之間的平衡有了變化時(也只有這時),YAGNI就會變成好的實踐。

總之,你不要花費任何精力在以後迭代中才需要的功能上。即使實現這個功能的成本可以忽略,你也不要這樣做,因爲這樣會增加修改成本。但是你只有在使用XP或者其它類似的降低變更成本的技術時纔可以遵循以上建議。

 

究竟什麼是簡單(What on Earth is Simplicity Anyway)

 

因此,我們希望程序能夠越簡單越好,這聽起來沒什麼好爭論的,畢竟有誰想要複雜呢?但問題來了,究竟“什麼是簡單呢?”

XPE一書中,Kent對簡單系統制定了四個評測標準,依序是(最重要排最前面):

l         通過所有測試(Runs all the Tests)

l         呈現所有的意圖(Reveals all the intention)

l         避免重複(No duplication)

l         最少數量的類或方法(Fewest number of classes or methods)

 

    通過所有測試是一項相當簡單的標準,避免重複也很明確,儘管許多開發人員需要別人的指點才能做到。比較麻煩的是“呈現所有的意圖”這一項,這到底指的是什麼呢?

    這句話的本意就是明確的代碼。XP對代碼的可讀性有着極高的重視。雖然在XP中,術語"clever code"已經被濫用,不過意圖清楚的代碼,對其他人來說是友善的(cleverness)

Josh KerievskyXP 2000論文中舉了一個很好的例子。他閱讀了在XP領域可能是大家最熟知的JUnit的程序源碼。JUnit使用裝飾模式(decorators)在測試用例(test cases)中加入可選功能,像是併發同步和批量”set up”代碼。將這些可選功能的代碼抽取到裝飾者(decorator)中,使得通用的代碼更加清晰。

但是你必須捫心自問,這樣做之後的程序真的簡單嗎?對我來說是,因爲我熟悉裝飾模式。但是對於不瞭解裝飾模式的人來說還是相當複雜的。類似的,JUnit使用的可插入方法(pluggable method)對大部分剛開始接觸的人來說根本不簡單清晰。所以,也許我們可以說JUnit的設計對有經驗的人來說是比較簡單的,但是對於新手來說則比較複雜。

    XP的“一次,並且僅有一次(Once and Only Once)”和《程序員修煉之道》提倡的DRY(不要重複自己)都專注在去除重複的代碼。這些建議都有很顯著而且驚人的效果。只要依照這個方式,你就可以避免重複。但是它不能解決所有問題,簡單化還是不容易做到的。

    最近我參與了一個可能被過度設計的項目。系統經過重構之後去除了部分靈活性設計。就像其中的一位開發者說的那樣“重構過度設計的系統要比重構沒有設計的系統容易的多”。做一個比你所需要的簡單一點的設計是最好的,但是稍微複雜一點點也不是什麼嚴重的事情。

我聽過最好的建議來自Bob大叔(Robert Martin)。他的建議是不要太在意什麼是最簡單的設計。畢竟以後你可以,應該,也會再重構它的。最後,不斷的重構,比知道怎樣才能做到最簡單的設計重要得多。

 

重構違反了YAGNI嗎?(Does Refactoring Violate YAGNI?)

 

這個主題最近出現在XP郵件列表中,當我們審視設計在XP中扮演的角色時,我覺得很值得拿出來討論。

基本上這個問題起因於重構需要耗費時間卻沒有增加新的功能。而YAGNI的觀點是你應該爲了眼前的需要做設計而不是未來,這樣算是相互抵觸嗎?

YAGNI的觀點是不要加入一些現階段不需要的複雜性,這也是簡單設計這條實踐的部分精神。重構可以保證你的設計儘可能的簡單,所以當你覺得可以讓系統變得更簡單的時候,就進行重構。

簡單設計這條實踐不僅僅是XP利用實踐而且也是XP啓動實踐。只有基於測試、持續集成和重構纔能有效的保證簡單設計。而同時,簡單設計又對於保持變更曲線平緩非常重要。任何不必要的複雜性都會讓系統變得難於調整,除非這個調整正是你加入複雜性所預料的調整。不過,人們通常不善於預料未來,所以最好還是努力地保持簡單性。同樣,人們也不太可能第一次就能做到最簡單,因此你需要重構來幫助你更接近這個目標。

 

模式與XP(Patterns and XP)

 

JUnit的例子讓我不得不想到模式。XP和模式之間的關係很微妙,也常常被問起。Joshua Kerievsky認爲模式在XP被過分輕視,而且他所提出的理由也相當令人信服,我不想再重複。不過值得一提的是,對於很多人來說模式似乎與XP是有衝突的。

    其實本質在於模式常常被過度的使用。世上有太多傳奇的程序員,第一次讀到四人幫以32行代碼闡述16種模式這樣的事情還記憶猶新。我還記得有一晚與Kent喝着酒一起討論一篇叫做《不要設計模式:23種簡單技巧》的文章。我們認爲像文章中那樣的情況使用if語句要好過策略模式。這個玩笑說明模式常常被濫用,但這並不表示模式是不足取的。問題在於你要怎樣運用它。

其中一種觀點是簡單設計的力量自然會將設計引向模式。很多重構的例子明確地這麼做了;甚至不用重構,你只要遵從簡單設計的規則就會發現模式,即使你還不知道模式是什麼。這種說法也許是真的,不過它真的是最好的方式嗎?當然,如果你對模式有個大略的瞭解,或者手邊有一本書可以參考,而不是自己去發明模式,那這是一種較好的方式。當我覺得快用到模式的時候,我肯定會去翻翻GOF的書。就我來說,有效的設計告訴我們,模式值得付出代價去學習——這是它特有的技能。同樣地就像Joshua所建議的,我們需要更熟悉於如何逐步地運用模式。XP使用模式的方式與一般使用模式的方式有所不同,但並沒有抹煞它的價值。

但是從一些郵件列表中的觀點看來,我覺得很多人認爲XP並不鼓勵使用模式,儘管XP的大部分倡導者也都是模式運動的領導者。是因爲他們已經超越了模式?或是因爲他們已經將模式融入了思想而不必再去體現它?我不知道其它人的答案是什麼,但是對我來說,模式仍然非常重要。XP也許可以認爲是一種開發過程,而模式則是設計技術的骨幹知識,不管採用哪種開發過程,這些知識都是很有用的。不同的過程使用模式的方式也許不同,XP強調直到需要時才使用模式並且通過簡單的實現逐步引入模式。所以模式仍然是一種必須獲得的關鍵知識。

我建議採用XP的人這樣來使用模式:

l         花點時間學習模式。

l         留意使用模式的時機(但是別太早)。

l         先關注如何以最簡單的方式使用模式,然後再慢慢增加複雜度。

l         如果你用了一種模式卻覺得沒有多大幫助——不用怕,再次把它去掉。

 

我認爲使用XP應該更強調學習模式。我不確定如何讓它和XP的實踐合適的搭配起來,不過我相信Kent會想出辦法來的。

 

發展架構(Growing an Architecture)

 

軟件架構是指什麼呢?對我來說,架構是系統核心元素(elements)的意思,這部分是難以改變的。剩下的都必須建造在這基礎上。

    那麼當你使用演進式設計時,架構又扮演着什麼樣的角色呢?XP的批評者再一次地聲稱XP忽視架構,因爲XP的路線是儘快地開始寫程序,相信重構會解決所有設計上的問題。有趣地是,這些批評者說得沒錯,這有可能是XP的缺點。的確,最激進的XP專家——像Kent BeckRon JeffriesBob Martin——盡其所能地避免任何預先的架構性的設計。在你真的要用到數據庫之前,不要加入數據庫,先用文本文件來代替,在之後的迭代中通過重構加入數據庫。

    我常被認爲是一個膽小的XP專家,這點我不同意。我認爲一個概括性的初始架構有它的用處所在。像是一開始要怎麼將應用分層,如何與數據庫交互(如果你需要的話),使用哪種方式去處理web server

    實際上,我認爲這些就是近年來我們所研究的模式。隨着你對模式認識的加深,你會越來越習慣於在開始時就考慮如何去適當的使用它們。不過,關鍵性的差異在於這些初始架構的決定是可以更改的,明確地說只要團隊意識到他們的判斷有誤時,就應該有勇氣去修正它們。有人跟我講過一個關於項目的故事,就在項目接近部署階段時,他們突然決定不再使用EJB,並且要將已有的應用從系統中移除。這是一個規模相當大的重構,不過最後還是順利的完成了。XP中的啓動實踐不僅讓事情變得可能,而且很值得去做。

    如果以相反的方式來做這件事呢?如果你決定不採用EJB,那麼將來會很難加入嗎?你是否真的要在嘗試過各種方法卻發現依然欠缺什麼時,才使用EJB?這是一個牽涉很多因素的問題。不使用複雜的組件當然可以增加系統的簡單度,而且可以讓項目進展得比較快。但有時候從系統中抽掉某個部分會比加入它要容易多了。

    所以我建議從評估架構可能的樣子開始。如果你看到將會有多個用戶操作大量的數據,那麼一開始就應該直接使用數據庫。如果你看到很複雜的業務邏輯,那麼就套用領域模型(domain model)。當你懷疑是否偏離了簡單性原則時,那就遵循YAGNI的精神。所以你要有所準備,當發現所使用的架構沒有任何幫助時應儘快的簡化它。

 

UMLXP(UML and XP)

 

在我投身於XP領域之後,我和UML間的關係使得一個很大的疑問一直揮之不去:這兩者能兼容嗎?

    它們之間有一些不兼容的地方。XP顯然在很大程度上不再重視畫圖。雖然官方的立場是“有用就用”,但是實際上卻隱藏着“真正的XP實踐者不需要畫圖”的潛臺詞。而且確實有很多人不習慣畫圖,就像Kent一樣,這加強了這種觀點。確實,我也從來沒見過Kent主動使用固定的標記法畫下軟件藍圖(software diagram)[譯註2]

我覺得這個問題有兩個獨立的原因。第一個原因是軟件藍圖對一些人來說有用,而對另一些人來說沒用。有危害的是,認爲軟件藍圖有用的人卻不是真正動手做的人,反之既然。我們應該接受並不是每個人都應該使用圖表(diagram)的事實。

    另一個原因是軟件藍圖常傾向於引入繁重的過程(heavyweight process),這些過程耗時費力卻不見得有用,甚至還會產生負面影響。我認爲應該教導人們如何適當有效的使用圖表並且避免落入繁重的陷阱,而不是像些XP專家說的那樣“只有懦弱的人才用”。

    所以,我對於適當有效的使用圖表的建議是:

    首先別忘了你畫這些圖的目的,主要的價值在於溝通。有效的溝通意味着突出重要的部分而忽略不太重要的部分。這樣的取捨也是有效運用UML的關鍵。不必把全部的類(class)都畫出來——只畫出重要的就可以了;對於每個類也不必顯示所有的屬性(attribute)和操作(operation)——只顯示重要的;也不要爲所有的用例(use case)和方案(scenarios)畫時序圖——只... 讓你瞭解大概的情況。在使用圖表時常犯的通病就是人們通常希望詳細完整的把圖表現出來。其實程序代碼就是提供完整信息的最佳來源,同時代碼本身也是保持信息同步的最簡單方式。面面俱到的圖表是一目瞭然的敵人。

    圖表通常的用途是在開始編寫代碼之前探討設計。在你的印象中常常覺得這樣的行爲在XP中是不合法的,但並不是這樣的。很多人都說當你遇到棘手的問題時,是值得先將它們彙總起來開一個快速設計會議(a quick design session)。當你進行設計會議時:

l         保持簡短。(keep them short)

l         不要涉及到所有的詳細(只挑重要的)。

l         把結果當作是草圖,而不是定案。

 

上面的最後一點值得深入探討。當採用了預先式設計,隨後而且往往是在編碼的時候,你會不可避免的發現一些設計錯誤。如果你適時變更設計,它就不是問題。麻煩的是當人們認爲設計已經定案時,他們不會將在編碼中獲得的知識反饋到設計中去。

    變更設計不代表一定要更改圖表。畫這些圖表來幫助你瞭解設計,然後就丟掉,這麼做是非常合理的。這些圖能有所幫助就有它的價值了,它們不必永遠存在。最好的UML圖是不會作爲歷史資料存在的。

    不少XP實踐者使用CRC卡片,這與UML並不衝突。我總是將CRC卡片和UML圖混在一起,哪個更適合手頭上的工作就選擇哪個。

UML圖的另一個用途是作爲持續維護的文檔資料。它一般的形式,就是在用例工具(case tool)中看到的模型。最初的想法是留着這樣的資料有助於構建系統。事實上卻常常沒什麼用。

l         保持圖表更新花費太多的時間,因此常無法與代碼保持同步。

l         它們被隱含在用例工具或者厚重的包裝(a thick binder) 中,以致被人忽略。

 

所以,希望這種持續維護的文檔資料起到作用,就要從這些已知的問題下手:

l         只用一些改起來不至於讓人覺得痛苦的圖。

l         把圖放在顯眼的地方。我喜歡畫在牆上,鼓勵大家一起動手修改。

l         注意這些圖是不是有人在用,沒用的就擦掉。

 

使用UML圖的最後一種方式是作爲移交工作時的文檔資料,比如說在不同團隊移交工作時。按照XP的觀點,產品的文檔就是素材(story),因此這些文檔的價值已經得到了客戶的肯定。於是UML又派上了用場,它所提供的圖形有助於溝通。別忘了程序代碼本身就蘊含了所有詳細的信息,所以圖形的作用只是提供概括以及突出重要的部分。

 

關於隱喻(On Metaphor)

 

好吧,我不妨公開的承認——我一直沒有抓住隱喻(metaphor)的精神。我知道它有用,而且在C3項目中運用得很好,但是這並不表示我知道怎麼用它,更不用說要解釋怎麼用了。

    XP實踐中的隱喻是建立在Ward Cunningham's爲系統命名的做法上。提出一些衆所周知的常用詞彙,然後用這個詞彙表(vocabulary)來比喻整個領域(domain)。這些代表系統特性的詞彙會出現在類和方法的命名上[譯註3]

    我曾經通過建造領域概念模型(conceptual model)來構造名字系統(system of names)。利用UML或者它的前身與領域專家一起建造概念模型。我發現你必須很小心的保持最精簡的符號集合,而且要當心別讓技術性的問題不知不覺的影響了這個模型。但是一旦你完成這個概念模型,你就可以爲這個領域建立一個詞彙表。這些詞彙很容易理解。領域專家可以用來與開發人員溝通。概念模型無法與類設計完美的吻合,但是已足夠給整個領域一個通用的詞彙表。

    目前我找不到任何理由說明爲何這個詞彙表不能運用比喻,就像C3中將工資單比喻爲工廠裝配線一樣;我也不覺得基於領域詞彙表建造你的名字系統有什麼壞處。我也不會放棄我可以駕輕就熟的系統命名方式。

    人們常批評XP是覺得一個系統至少需要一些籠統的設計。XP實踐者們則常以“就是隱喻啊”來響應。但是我一直沒有看到一個對於隱喻令人信服的解釋。這是XP的空缺,需要由XP實踐者們來理出頭緒。

 

   你想成爲架構師嗎?(Do you wanna be an Architect when you grow up?)

 

近幾年來“軟件架構師(software architect)”越來越熱門,這是一個就我個人而言難以接受的術語。我太太是建築工程師。工程師和架構師之間的關係是... 有趣的。我最喜歡的一句話是:架構師喜好三種東西:球狀物、灌木叢和鳥。因爲架構師畫出這些美麗的圖畫,卻要工程師保證能全都做出來。所以我避免使用軟件架構師一詞,畢竟如果連我的太太都不能尊重我的專業,我又怎麼能對其他人有所期望呢?

在軟件行業,架構一詞可以代表很多含義。(軟件行業中幾乎所有的詞都可以代表很多含義。) 概括爲一句話是:我不是一名程序員——我是一名架構師。還可以進一步解譯成:我現在是一名架構師——我這麼重要怎麼能參與編碼。然後這個問題就變成了:是否一定要徹底脫離編碼,才能與你的技術領導者的地位相符合?

這個問題引起衆多的不滿。大家一想起再也無法擔任架構師這個角色就非常生氣。我經常聽到這樣的埋怨:在XP中沒有給經驗豐富的架構師揮灑的空間。

就設計本身來說,我不認爲XP不重視經驗或者好的設計技術。事實上,我從很多XP提倡者——Kent BackBob Martin當然還有Ward Cunningham——那裏學到了設計的理念。然而這也代表着他們的角色從大家既有的印象中開始轉變成爲技術領導者。

    我將以一位ThoughtWorks的技術領導者Dave Rice爲例。Dave曾參與過幾次完整地開發,並且曾在一個50人的項目中擔任非正式的技術主管。他擔任主管的角色意味着要花很長的時間與程序員爲伍。他會和需要幫助的程序員一起工作,否則就留意着看誰需要幫助。他的座位上有一個明顯的標記。在ThoughtWorks很長一段時間,他可以適應任何形式的辦公環境。他曾經與發行經理(release manager)Cara共享辦公室一段時間。而在最後的幾個月,他搬到了程序員們工作的開放式房間(就像XP實踐者喜歡的開放式“戰鬥場所”)。這麼做對他很重要,因爲他可以知道事情的進展,並適時伸出援手。

熟悉XP的人已經意識到我描述的是XP中的教練(Coach)角色。的確,在XP玩的文字遊戲中將技術領導叫做教練。意義在於:在XP中技術領導者的作用是通過教導程序員和幫助他們做決定而體現出來的。這需要良好的人際關係以及高超的技術。Jack BollesXP2000上說:孤立的大師只會作繭自縛,合作和教導纔是成功的關鍵。

在研討會的晚宴上,我和Dave與一位對XP持反對觀點的人談話。當我們討論到以前的經驗,我們的方法相當的類似。我們都偏好自適應的(adaptive)和迭代式(iterative)開發,也認爲測試是重要的。所以我們對他反對的立場感到疑惑。然而當他說“最後的時候我會讓程序員照着設計進行重構”。事情一下子明朗起來。後來Dave又對我解釋“如果他不信任他的程序員,又何必要僱用他們呢?”,觀念上的隔閡就更加清楚了。在XP裏頭,有經驗的開發人員所能做的最重要的一件事就是儘量將所有技術傳遞給更多的新手。你讓教練來指導開發人員做出重大的決定,而不是由一個架構師來決定這一切。就像Ward Cunningham指出的那樣,這麼做使得他的技術得到廣泛使用,這對項目的好處大於一個孤膽英雄所能做的。

 

可逆性(Reversibility)

 

XP2002大會上,對於敏捷方法(agile methods)和精益生產(lean manufacturing)之間的關係,Enrico Zaninotto發表了一場令人陶醉的演說。按照他的觀點,他認爲兩種方法中都有一個關鍵特徵:它們都通過減少過程中的不可逆性(irreversibility)來降低複雜度

從這個觀點來看,造成複雜度的主要因素之一是在項目中做出了不可逆的決策。如果你可以輕易的改變你做出的決策,這意味着讓決策恢復正常不會對項目有太大的影響——你的生活變得簡單多了。所以,在演進式設計中設計師們應該想方設法避免在設計中產生不可逆性。與其急於嘗試着去做出一個正確的設計,倒不如從下面兩條路中選擇一條:推遲設計(直到你有了足夠多的信息)或者做出一個讓你在不遠的將來可以很容易推翻逆轉的設計。

支持可逆性的決定就是敏捷方法強調使用源代碼管理系統的原因之一。雖然它不能保證可逆性,特別是對很久以前做出的決策,但是它爲團隊提供了一個可以信賴的基礎,即使很少去使用它。

可逆性設計也包含着一個可以很快發現錯誤的過程。迭代開發的一個重要作用就在於快速的迭代過程允許客戶看着系統逐步成長,並且如果在需求中發現錯誤,可以很快地確定問題並做出修改,而不至於將錯誤堆積到讓人望而卻步的地步。對於設計來說快速的定位錯誤同樣重要。這意味着你要對設計做一些裝置(set things up),以便對存在潛在問題的部分做出快速的測試。這還意味着值得通過原型來模擬系統的一部分,以便試驗對設計做出變更的困難度,即使你現在還不需要真的去變更。幾個小組通過在原型中較早的試驗變更,來評估對設計做出變更的困難度。

 

設計自律(The Will to Design)

 

我已經在這片文章中提到了很多技術實踐,而人的因素則太容易被忽略了。

演進式設計在工作中需要驅動它集中於一點的力量。這個力量僅僅能夠來自於團隊中那些能夠保證高水平設計的人員。

設計自律並不是來自於團隊中每個人(儘管如果是這樣會很好),通常在團隊中只有一到兩個人對全部的設計負責。這往往被認爲是架構師擔當的責任。

對設計負責就意味着要一直監視着代碼,注意代碼是否開始變得骯髒,並在局面失去控制之前採取快速的行動進行糾正。負責審查設計的人員並不一定要參與修改——但是他們一 定要確保有人來修改。

缺少自律的設計被認爲是演進式設計失敗的重要原因。即使人們對我在這片文章中提到的實踐非常熟悉,沒有自律的設計還是無法成功的。

 

很難重構的東西(Things that are difficult to refactor in)

 

我們能用重構來處理所有設計方面的決策嗎?或者,是否存在一些問題因爲瀰漫在整個系統中而難以在將來加入設計中?XP的正統觀念是:當你需要時,任何事情都可以很容易的加入,所以YAGNI總是能夠適用。我想知道如果存在意外情況呢?有一個不錯的例子是軟件的國際化問題。這是不是一種現在應該立即進行,否則以後再加入時會覺得痛苦的事情?

我能很容易想到一些會陷入這種境地的事情。然而事實上我們仍然瞭解的太少。如果你必須加入一些功能,如國際化,那麼隨後你就會十分清楚它要花費多少成本。但你不容易在真正應用它之前就搞清楚,周復一週的完善和維護它要花費多少成本。同樣,你也不容易意識到你可能已經做錯了,這樣無論如何也要做些重構了。

部分能夠爲YAGNI辯護的理由是,許多潛在的需求到最後並不真的需要,至少不是你預料的那種方式。不過對潛在的需求不做任何事情能節省下的力氣,遠沒有通過重構來達到實際需要花費的力氣多。

另外一個要想的問題是你是否真的知道如何去做。如果你已經有做過幾次軟件國際化的經驗,你會知道該使用什麼樣的模式。同樣的,你更可能取得成功。如果你對將要加入的構件(structures)有一定的經驗,則多半會比第一次處理這種問題效果好的多。所以我的建議是,如果你知道應該怎麼去做,你就要考慮現在做和將來做,兩種情形之間不同的成本。然而,如果你沒有處理過類似的問題,不僅是你無法正確評估需要的成本,而且你也不太可能把事情作好。這種情形,你就要選擇將來再做。如果你在將來的某一天做了,而且嚐到了苦頭,則你要知道這比在早期加入它的情況好的多了。當你的團隊更有經驗,你對相關領域有更多認識,你對需求也更瞭解。通常到這時你回頭看纔會發現事情有多簡單。提早加入的設計比你想象中要難多了。

這個問題也跟素材的順序密切相關。在Planning XP一書中,Kent和我公開的指出我們的歧見。Kent偏向於只讓業務價值這一個因素影響素材的順序。在最初的爭論過後,Ron Jeffries也同意了這種想法。我仍保持懷疑。我認爲應該在業務價值和技術風險之間找一個平衡點。基於這樣的理由讓我提前爲軟件國際化做準備以降低風險。但是這種做法只有當第一次釋放版本就需要將軟件國際化時才能成立。儘快地釋放版本是非常重要的。任何在第一次釋放版本中不需要的附加複雜性,值得在第一次版本釋放後再做。發行之後運行的程序有強大的力量,它抓住了客戶的注意力,增加了信任感並且是一個學習的好機會。要盡你一切努力來靠近第一次發行的日期。即使在初次版本釋放之後添加某些功能會花費更多的成本,還是要保證提早釋放版本。

與任何新技術一樣,XP很普通,以至於它的擁護者都不太清楚它的使用限制條件。許多XP實踐者被告知演進式設計是不可能用於解決確定的問題,不料竟發現這是完全可能的。征服了“不可能”的情況,於是認爲所有這一類問題都可以解決。當然我們不能籠統的概括XP的使用界限。直到XP社團偶爾碰到了界限並且慘遭失敗,我們才能確信這是XP的使用界限。而且我們有權利去嘗試超越這個潛在的界限。

(在Jim Shore近期的文章中討論了一些問題,其中就包括國際化問題,這個潛在的界限最終被清除了。)

 

產生設計了嗎?(Is Design Happening?)

 

    演進式設計的一個難點是,很難分辨出設計是否已經發生。將設計與編碼混合在一起有不去設計就編碼的危險——這是造成演進式設計發散和失敗的情形。

    如果你在開發團隊中,你就可以按照代碼的質量來判斷是否產生了設計。如果代碼變得越來越複雜、維護越來越困難,這說明缺少足夠的設計。但是遺憾的是,這只是主觀觀點。我們還沒有一套可靠的標準來客觀的衡量設計的質量。

    如果缺少能見度(visibility)對於技術人員來說是困難的,那麼對於團隊中的非技術人員來說則是更讓人擔憂的。如果你是一名經理或者一名客戶,你會怎麼看待設計良好的軟件?你應該認爲是重要的,因爲設計糟糕的軟件將來變更的成本太昂貴了。到底什麼是不好的設計,這很難回答,但是下面給出了一些提示:

l         和技術人員交談。如果他們抱怨做出變更非常困難,那麼請重視這個問題並且給他們修改的時間。

l         密切注視有多少代碼別遺棄掉。在重構正常的項目中,被刪除的代碼數量會比較穩定。如果在一段時間內沒有任何代碼被刪除,這無疑表示沒有充分的進行重構——這會造成設計退化。然而,像所有的規則一樣,這個可能被濫用,因此建議信任技術高超的技術人員勝於任何規則,儘管這是主觀的。

 

這樣來看設計死了嗎?(So is Design Dead?)

 

設計死了嗎?絕對沒有,只是設計的本質發生了變化。XP的設計追求以下的技巧:

l         持續保持代碼乾淨、簡單。

l         重構,使你覺得任何有必要的時候都可以大膽的改進。

l         對模式有深刻的認識:不只死搬硬套,更要知道該何時用,以及如何逐步引入。

l         着眼於應付未來變化的設計,知道現在做出的決策可能要過後進行修改。

l         知道如何將設計傳達給必要的人,用代碼、圖表和最重要的:交談。

 

以上挑出來的技巧看來都挺嚇人,但是要成爲一個優秀的設計師本來就很難。至少我不覺得,XP讓它變得簡單些。但是我想XP讓我們對有效率的設計有全新的看法,因爲它讓演進式設計成爲一種可行的方式。而且我也很支持演進——否則誰知道我在幹什麼呢?

 

致謝(Acknowledgements)

     

過去的這兩年裏,我從很多好朋友身上偷學到不少好的想法,很多都已經記不起來了。但是我記得從Joshua Kerievski那裏偷到的好東西。我也記得Fred GeorgeRon Jeffries給我很好的建議。我當然也不能忘記來自WardKent的很多好的想法。

我也感謝曾經提出問題和指出打字錯誤的朋友。由於我的疏忽沒有保留這些朋友的列表,但是我記得有Craig JonesNigel ThorneSven GortsHilary NelsonTerry Camerlengo

 

修訂記錄(Revision History)

 

以下是這篇文章重大的修改記錄:

20045月:添加了”The Will to Design””Reversibility””Is Design Happening”三個章節。

    20012月:對growing an architecturethe role of an architect、和where things that are difficult to add with refactoring等段落做修改。

   20007月:原始文件在XP 2000發表,並刊載於MartinFowler.com

 

 

[譯註1]:所謂XP利用實踐(exploiting practices)是建立於軟件變更曲線平滑的情況下的,而XP啓動實踐(enabling practices)則是XP建立這個基礎的方式。由於不知道國內如何來翻譯者兩個名詞的,所以姑且這樣翻譯吧。

[譯註2]:軟件藍圖(software diagram)的翻譯有點牽強,其實本意就是表達軟件設計意圖的圖表。

[譯註3]:說到隱喻,記得以前我在做一個文件管理腳本程序時,就將不同責任的腳本文件比喻成不同的狗,負責遍歷目錄的叫做偵探犬,負責記錄的叫做檔案狗……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章