【?第三部分?】通過重構(gòu)來加深理解
第8章:突破

一般來說,持續(xù)重構(gòu)讓事物逐步變得有序。代碼和模型的每一次精化都讓開發(fā)人員有了更加清晰的認識。這使得理解上的突破成為可能。之后,一系列快速的改變得到了更符合用戶需要并更加切合實際的模型。其功能性及說明性急速增強,而復(fù)雜性卻隨之消失。這種突破不是某種技巧,而是一個事件。它的困難之處在于你需要判斷發(fā)生了什么,然后再決定如何處理。
當突破帶來更深層次的模型時,通常會令人感到不安。與大部分重構(gòu)相比,這種變化的回報更多,風險也更高。而且突破出現(xiàn)的時機可能很不合時宜。盡管我們希望進展順利,但往往事與愿違。過渡到真正的深層次模型需要從根本上調(diào)整思路,并且對設(shè)計做大幅修改。在很多項目中,建模和設(shè)計工作最重要的進展都來自于突破。
不要試圖去制造突破,那只會使項目陷入困境。通常,只有在實現(xiàn)了許多適度的重構(gòu)后才有可能出現(xiàn)突破。在大部分時間里,我們都在進行微小的改進,而在這種連續(xù)的改進中模型深層含義也會逐漸顯現(xiàn)。
要為突破做好準備,應(yīng)專注于知識消化過程,同時也要逐漸建立健壯的UBIQUITOUS LANGUAGE。尋找那些重要的領(lǐng)域概念,并在模型中清晰地表達出來。精化模型,使其更具有柔性。提煉模型。利用這些更容易掌握的手段使模型變得更清晰,這通常會帶來突破。
不要猶豫著不去做小的改進,這些改進即使脫離不開常規(guī)的概念框架,也可以逐漸加深我們對模型的理解。不要因為好高騖遠而使項目陷入困境。只要隨時注意可能出現(xiàn)的機會就夠了。
【學(xué)習心得】:我自己手頭上目前持有一個運行了近十年的千萬級用戶系統(tǒng),至今還在持續(xù)運營和追加功能。對于以上那些突破的感受,我太有體會了。整個項目總共經(jīng)歷了兩次大的突破,以及無數(shù)次小突破。而每一次突破都十分痛苦,但快樂著。離不開團隊的堅持,離不開團隊的持續(xù)學(xué)習,更離不開團隊吃苦精神。
第9章:將隱式概念轉(zhuǎn)變?yōu)轱@示概念
深層建模聽起來很不錯,但是我們要如何時間它呢?深層模型之所以強大是因為它包含了領(lǐng)域的核心概念和抽象,能夠以簡單靈活的方式表達出基本的用戶活動、問題以及解決方案。
若開發(fā)人員識別出設(shè)計中隱含的某個概念或者在討論中收到啟發(fā)而發(fā)現(xiàn)一個概念時,就會對領(lǐng)域建模和響應(yīng)的代碼進行許多轉(zhuǎn)換,在模型中加入一個或多個對象或關(guān)系,從而將此概念顯示地表達出來。有時,這種從隱式概念到顯示概念的轉(zhuǎn)換可能就是一次突破。
概念挖掘
1、傾聽領(lǐng)域?qū)<沂褂玫恼Z言。
有沒有一些術(shù)語能夠簡潔地表達出復(fù)雜的概念?他們有沒有糾正過你的用詞(也許是很委婉的提醒)?當你使用某個特定詞語時,他們臉上是否已經(jīng)不再流露出迷惑的表情?這些都是暗示了某個概念也許可以改進模型。
2、檢查不足之處。
你所需要的概念并不總是浮在表面上,也絕不僅僅是通過對話和文檔就能讓它顯現(xiàn)出來。有些概念可能需要你自己去挖掘和創(chuàng)造。要挖掘的地方就是設(shè)計中最不足的地方,也就是操作復(fù)雜且難于理解的地方。每當有新需求時,似乎都會讓這個地方變得更加復(fù)雜。有時,你很難意識到模型中丟失了什么概念。也許你的對象能夠?qū)崿F(xiàn)所有的功能,但是有些職責的實現(xiàn)卻很笨拙。而有時,你雖然能夠意識到模型中丟失了某些東西,但是卻無法找到解決方案。
3、思考矛盾之處。
由于經(jīng)驗和需求的不同,不同的領(lǐng)域?qū)<覍ν瑯拥氖虑闀胁煌目捶?。即使是同一個人提供的信息,仔細分析后也會發(fā)現(xiàn)邏輯上不一致的地方。在挖掘程序需求的時候,我們會不斷遇到這種令人煩惱的矛盾,但它們也為深層模型的實現(xiàn)提供了重要線索。有時矛盾只是術(shù)語說法上的不一致,有些則是由于誤解而產(chǎn)生的。但還有一種情況是專家們會給出相互矛盾的兩種說法。
4、查閱書籍。
在尋找模型概念時,不要忽略一些顯而易見的資源。在很多領(lǐng)域中,你都可以找到解釋基本概念和傳統(tǒng)思想的書籍。你依然需要與領(lǐng)域?qū)<液献鳎釤捙c你的問題相關(guān)的那部分知識,然后將其轉(zhuǎn)化為適用于面向?qū)ο筌浖母拍?。但是,查閱書籍也許能夠使你一開始就形成一致且深層的認識。
5、嘗試,再嘗試。
并不是所有這些方向性的改變都毫無用處。每次改變都會把開發(fā)人員更深刻的理解添加到模型中。每次重構(gòu)都使設(shè)計變得更靈活且為那些可能需要修改的地方做好準備。我們其實別無選擇。只有不斷嘗試才能了解什么有效什么無效。企圖避免設(shè)計上的失誤將會導(dǎo)致開發(fā)出來的產(chǎn)品質(zhì)量劣質(zhì),因為沒有更多的經(jīng)驗可用來借鑒,同時也會比進行一系列快速實驗更加費時。
如何為那些不太明顯的概念建模?
1、顯示的約束。
約束是模型概念中非常重要的類別。它們通常是隱含的,將它們顯式地表現(xiàn)出來可以極大地提高設(shè)計質(zhì)量。

(例子)為顯示表達超訂策略而重構(gòu)的模型??
2、將過程建模為領(lǐng)域?qū)ο蟆?/b>
首先要說明的是,我們都不希望過程變成模型的主要部分。對象是用來封裝過程的,這樣我們只需要考慮對象的業(yè)務(wù)目的或意圖就可以了。就像我們以上用來安排貨運路線的運輸系統(tǒng)例子,安排路線的過程具有業(yè)務(wù)意義。SERVICE是顯示表達這種過程的一種方式,同時它還會降異常復(fù)雜的算法封裝起來。
如果過程的執(zhí)行有多種方式,那么我們也可以用另一種方法來處理它,那就是將算法本身或其中的關(guān)鍵部分放到一個單獨的對象中。這樣,選擇不同的過程就變成了選擇不同的對象,每個對象都表示一種不同的STRATEGY(策略)。
那么,過程是應(yīng)該被顯示表達出來,還是應(yīng)該被隱藏起來呢?區(qū)分的方法很簡單:它是經(jīng)常被領(lǐng)域?qū)<姨崞鹉?,還是僅僅被當作計算機程序機制的一部分?
約束和過程是兩大類概念模型,當我們用面向?qū)ο笳Z言編程時,不會立即想到它們,然而它們一旦被我們視為模型元素,就真的可以讓我們的設(shè)計更為清晰。
3、模式:SPECIFICATION

業(yè)務(wù)規(guī)則通常不適合作為ENTITY和VALUE OBJECT的職責,而且規(guī)則的變化和組合也會被掩蓋領(lǐng)域?qū)ο蟮幕竞x。但是將規(guī)則移出領(lǐng)域?qū)拥慕Y(jié)果會更糟糕,因為這樣一來,領(lǐng)域代碼就不再表達模型了。
邏輯編程提供了一個概念,即“謂詞”這種可分離、可組合的規(guī)則對象,但是要把這種概念用對象完全實現(xiàn)是很麻煩的。同時,這種概念過于通用,在表達設(shè)計意圖方面,它的針對性不如專門的設(shè)計那么好。
因此:為特殊目的創(chuàng)建謂詞形式的顯式的VALUE OBJECT。SPECIFICATION就是一個謂詞,可用來確定對象是否滿足某些標準。
【學(xué)習心得】:項目的溝通成本之大,正是因為許多人的內(nèi)心都會有一顆“自尊心”,我的領(lǐng)域我最懂,我的技術(shù)牛逼,你們業(yè)務(wù)人員可以一邊站。所以,想成為一個合格的團隊成員,至少得讓自己能成為一個合格的聆聽者??此坪唵危以谏钪芯桶l(fā)現(xiàn)了自己并非一位很好的聆聽者。錯過了許多對自己有用的信息,更多還是用了許多自以為是的拍腦袋決策。
第10章:柔性設(shè)計

為了使項目能夠隨著開發(fā)工作的進行加速前進,而不會由于它自己的老化將停滯不前,設(shè)計必須要讓人們樂于使用,而且易于做出修改。這就是柔性設(shè)計(supple design)。
很多過度設(shè)計(overengineering)借著靈活性的名義而得到合理的外衣。但是,過多的抽象層和間接設(shè)計常常成為項目的絆腳石??匆幌抡嬲秊橛脩魩韽姶蠊δ艿能浖O(shè)計,你常常會發(fā)現(xiàn)一些簡單的東西。簡單并不容易做到。
模式:INTENTION-REVEALING INTERFACES(意圖揭示接口)
如果開發(fā)人員為了使用一個組件而必須要去研究它的實現(xiàn),那么就失去了封裝的價值。當某個人開發(fā)的對象或操作被別人使用時,如果使用這個組件的新的開發(fā)者不得不根據(jù)其實現(xiàn)來推測其用途,那么他推測出來的可能并不是那個操作或類的主要用途。如果這不是那個組件的用途,雖然代碼暫時可以工作,但設(shè)計的概念基礎(chǔ)已經(jīng)被誤用了,兩位開發(fā)人員的意圖也是背道而馳。
因此:在命名類和操作時要描述它們的效果和目的,而不是表露它們是通過何種方式達到目的的。這樣可以使客戶開發(fā)人員不必去理解內(nèi)部細節(jié)。這些名稱應(yīng)該與UBIQUITOUS LANGUAGE保持一致,以便團隊成員可以迅速推斷出它們的意義。在創(chuàng)建一個行為之前先為它編寫一個測試,這樣可以促使你站在客戶開發(fā)人員的角度上來思考它。所有復(fù)雜的機制都應(yīng)該封裝到抽象接口的后面,接口只表明意圖,而不表明方式。

模式:SIDE-EFFECT-FREE FUNCTION(無副作用功能)
大多數(shù)操作都會調(diào)用其他的操作,而后者又會調(diào)用另外一些操作。一旦形成這種任意深度的嵌套,就很難預(yù)測調(diào)用一個操作將要產(chǎn)生的所有后果。第二層和第三層操作的影響可能并不是客戶開發(fā)人員有意為之的,也是它們就變成了完全意義上的副作用(任何對未來操作產(chǎn)生影響的系統(tǒng)狀態(tài)改變都可以成為副作用)。
多個規(guī)則的相互作用或計算的組合產(chǎn)生的結(jié)果是很難預(yù)測的。開發(fā)人員在調(diào)用一個操作時,為了預(yù)測操作的結(jié)果,必須理解它的實現(xiàn)以及它所調(diào)用的其他方法的實現(xiàn)。如果開發(fā)人員不得不“揭開接口的面紗”,那么接口的抽象作用就受到了限制。如果沒有了可以安全地預(yù)見到結(jié)果的抽象,開發(fā)人員就必須限制“組合爆炸”,這就限制了系統(tǒng)行為的豐富性。
因此:盡可能把程序的邏輯放到函數(shù)(返回結(jié)果而不產(chǎn)生副作用的操作稱為函數(shù))中,因為函數(shù)是只返回結(jié)果而不產(chǎn)生明顯副作用的操作。嚴格地把命令(引起明顯的狀態(tài)改變的方法)隔離到不返回領(lǐng)域信息的、非常簡單的操作中。當發(fā)現(xiàn)了一個非常適合承擔復(fù)雜邏輯職責的概念時,就可以把這個復(fù)雜邏輯移到VALUE OBJECT中,這樣可以進一步控制副作用。

模式:ASSERTION(斷言)
把復(fù)雜的計算封裝到SIDE-EFFECT-FREE FUNCTION中可以簡化問題,但實體仍然會留有一些有副作用的命令,使用這些ENTITY的人必須了解使用這些命令的后果。
如果操作的副作用僅僅是由它們的實現(xiàn)隱式定義的,那么在一個具有大量相互調(diào)用關(guān)系的系統(tǒng)中,起因和結(jié)果會變得一團糟。理解程序的唯一方式就是沿著分支路徑來跟蹤程序的執(zhí)行,封裝完全失去了價值。跟蹤具體的執(zhí)行也使抽象失去了意義。
因此:把操作的后置條件和類及AGGREGATE的固定規(guī)則表達清楚。如果在你的編程語言中不能直接編寫ASSERTION,那么就把它們編寫成自動的單元測試。還可以把它們寫到文檔或圖中(如果符合項目開發(fā)風格的話)。尋找在概念上內(nèi)聚的模型,以便使開發(fā)人員更容易推斷出預(yù)期的ASSERTION,從而加快學(xué)習過程并避免代碼矛盾。
INTENTION-REVEALING INTERFACE清楚地表明了用途,SIDE-EFFECT-FREE FUNCTION和ASSERTION使我們能夠更準確地預(yù)測結(jié)果,因此封裝和抽象更加安全。
模式:CONCEPTUAL CONTOUR(概念輪廓)
如果把模型或設(shè)計的所有元素都放在一個整體的大結(jié)構(gòu)中,那么它們的功能就會發(fā)生重復(fù)。外部接口無法給出客戶可能關(guān)心的全部信息。由于不同的概念被混合在一起,它們的意義變得很難理解。
而另一方面,把類和方法分解開也可能是毫無意義的,這會使客戶更復(fù)雜,迫使客戶對象去理解各個細微部分是如何組合在一起的。更糟的是,有的概念可能會完全丟失。鈾原子的一半并不是鈾。而且,粒度的大小并不是唯一要考慮的問題,我們還要考慮粒度是在哪種場合下使用的。
因此:把設(shè)計元素(操作、接口、類和AGGREGATE)分解為內(nèi)聚單元,在這個過程中,你對領(lǐng)域中一切重要劃分的直觀認識也要考慮在內(nèi)。在連續(xù)的重構(gòu)過程中觀察發(fā)生變化和保證穩(wěn)定的規(guī)律性,并尋找能夠解釋這些變化模式的底層CONCEPTUAL CONTOUR。使模型與領(lǐng)域中那些一致的方面(正是這些方面使得領(lǐng)域成為一個有用的知識體系)相匹配。
我們的目標是得到一組可以在邏輯上組合起來的簡單接口,使我們可以用UBIQUITOUS LANGUAGE進行合理的表述,并且使那些無關(guān)的選項不會分散我的注意力,也不增加維護負擔。但這通常是通過重構(gòu)才能得到的結(jié)果,很難在前期就實現(xiàn)。而且如果僅僅是從技術(shù)角度進行重構(gòu),可能永遠也不會出現(xiàn)這種結(jié)果;只有通過重構(gòu)得到更深層次的理解,才能實現(xiàn)這樣的目標。
INTENTION-REVEALING INTERFACE使客戶能夠把對象表示為有意義的單元,而不僅僅是一些機制。SIDE-EFFECT-FREE FUNCTION和ASSERTION使我們可以安全地使用這些單元,并對它們進行復(fù)雜的組合。CONCEPTUAL CONTOUR的出現(xiàn)使模型的各個部分變得更加穩(wěn)定,也使得這些單元更直觀,更易于使用和組合。
模式:STANDALONE CLASS(獨立的類)
即使是在MODULE內(nèi)部,設(shè)計也會隨著依賴關(guān)系的增加而變得越來越難以理解。這加重了我們的思考負擔,從而限制了開發(fā)人員能處理的設(shè)計復(fù)雜度。隱式概念比顯式引用增加的負擔更大了。
低耦合是對象設(shè)計的一個基本要素。盡一切可能保持低耦合。把其他所有無關(guān)概念提取到對象之外。這樣類就變得完全獨立了,這就使得我們可以單獨地研究和理解它。每個這樣的獨立類都極大地減輕了因理解MODULE而帶來的負擔。
盡力把最復(fù)雜的計算提取到STANDALONE CLASS(獨立的類)中,實現(xiàn)此目的的一種方法是從存在大量依賴的類中將VALUE OBJECT建模出來。低耦合是減少概念過載的最基本方法。獨立的類是低耦合的極致。
模式:CLOSURE OF OPERATION(閉合操作)
兩個實數(shù)相乘,結(jié)果仍為實數(shù)(實數(shù)是所有有理數(shù)和所有無理數(shù)的集合)。由于這一點永遠成立,因此我們說實數(shù)的“乘法運算是閉合的”:乘法運算的結(jié)果永遠無法脫離實數(shù)這個集合。當我們對集合中的任意兩個元素組合時,結(jié)果仍在這個集合中,這就叫做閉合操作。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ——The Math Forum,Drexel University
加法運算是實數(shù)集中的閉合運算。數(shù)學(xué)家們都極力避免去引入無關(guān)的概念,而閉合運算的性質(zhì)正好為他們提供了這樣一種方式。
因此:在適當情況下,在定義操作時讓它的返回類型與其參數(shù)的類型相同。如果實現(xiàn)者(implementer)的狀態(tài)在計算中會被用到,那么實現(xiàn)者實際上就是操作一個參數(shù),因此參數(shù)和返回值應(yīng)該與實現(xiàn)者有相同的類型。這樣的操作就是在該類型的實例集合中的閉合操作。閉合操作提供了一個高層接口,同時又不會引人對其他概念的任何依賴。
【學(xué)習心得】:經(jīng)歷了(還在經(jīng)歷)一個近十年的項目,我想自己還是比較有資格談?wù)勅嵝栽O(shè)計的感受。沒有學(xué)習這些柔性概念之前,我們能持續(xù)高效并運行開發(fā)一個項目那么長時間,功勞歸于一個重要的原則:簡單。起初,整個團隊都缺乏以上這些實用的模式理論作為參考,但大家都有秉承著一個“簡單”的共同原則,其實不知不覺中摸著石頭過河,在無數(shù)次重構(gòu)中逐漸跟以上模式契合起來。當然,如果我們能提前認識這些基礎(chǔ)的理論基礎(chǔ)知識,我想不必要的彎路會少走許多。也當然,系統(tǒng)還在不斷完善中,現(xiàn)在認識也不晚。
第11章:應(yīng)用分析模式
在《分析模式》一書中,Martin Fowler這樣定義分析模式:
分析模式是一種概念集合,用來表示業(yè)務(wù)建模中的常見結(jié)構(gòu)。它可能只與一個領(lǐng)域有關(guān),也可能跨多個領(lǐng)域。
Fowler所提出的分析模式來自于實踐經(jīng)驗,因此只要用在合適的情形下,它們會非常實用。對于那些面對著具有挑戰(zhàn)性領(lǐng)域的人們,這些模型為他們的迭代開發(fā)過程提供了一個非常有價值的起點。“分析模式”這個名字本身就強調(diào)了其概念本質(zhì)。分析模式不是技術(shù)方案,他們只是參考,用來指導(dǎo)人們設(shè)計特定領(lǐng)域中的模型。
分析模式最大的作用是借鑒其他項目的經(jīng)驗,把那些項目中有關(guān)設(shè)計方向和實現(xiàn)結(jié)構(gòu)的廣泛討論與當前模型的理解結(jié)合起來。脫離具體的上下文來討論模型思想不但難以落地,而且還會造成分析與設(shè)計嚴重脫節(jié)的風險,而這一點正是MODEL-DRIVEN DESIGN堅決反對的。
當你可以幸運地使用一種分析模式時,它一般并不會直接滿足你的需求。但它為你的研究提供了有價值的線索,而且提供了明確抽象的詞匯。它還可以知道我們的實現(xiàn),從而省去很多麻煩。
我們應(yīng)該把所有分析模式的知識融入知識消化和重構(gòu)的過程中,從而形成更深刻的理解,并促進開發(fā)。當我們應(yīng)用一種分析模式時,所得到的結(jié)果通常與該模式的文獻中記載的形式非常想像,只是因具體情況不同而略有差異。但有時完全看不出這個結(jié)果與分析模式本身有關(guān),然而這個結(jié)果仍然是受該模式思想的啟發(fā)而得到的。
但有一個誤區(qū)是應(yīng)該避免的。當使用眾所周知的分析模式中的術(shù)語時,一定要注意,不管其表面形式的變化有多大,都不要改變它所表示的基本概念。這樣做有兩個原因,一是模式中蘊含的基本概念將幫助我們避免問題,二是(也是更重要的原因)使用被廣泛理解或至少是被明確理解的術(shù)語可以增強UBIQUITOUS LANGUAGE。如果在模型的自然演變過程中模型的定義也發(fā)生改變,那么就要修改模型名稱了。
【學(xué)習心得】:本章節(jié)主要還是借助《分析模式》一書中的例子,用實踐例子來分析系統(tǒng)是如何在演繹過程使用模型的。這種科學(xué)謹慎的做法,才是一個工程師的基本觀念要求。
第12章:將設(shè)計模式應(yīng)用于模型
在《設(shè)計模式》中,有些(但并非所有)模式可用作領(lǐng)域模式,但在這樣使用的時候,需要變換一下重點。有些模式反映了一些在領(lǐng)域中出現(xiàn)的深層概念。這些模式都有很大的利用價值。為了在領(lǐng)域驅(qū)動設(shè)計中充分利用這些模式,我們必須同時從兩個角度看待它們:從代碼的角度來看它們是技術(shù)設(shè)計模式,從模型的角度來看它們就是概念模式。
模式:STRATEGY(也稱POLICY)

策略模式:定義了一組算法,將每個算法封裝起來,并使它們可以互換。STRATEGY允許算法獨立于使用它的客戶而變化[Gamma et al. 1995]
領(lǐng)域模型包含一些并非用于解決技術(shù)問題的過程,將它們包含進來是因為它們處理問題領(lǐng)域具有實際的價值。當必須從多個過程中進行選擇時,選擇的復(fù)雜性再加上多個過程本身的復(fù)雜性使局面失去控制。
因此:我們需要把過程中的易變部分提取到模型的一個單獨的“策略”對象中。將規(guī)則與它所控制的行為區(qū)分開。按照STRATEGY設(shè)計模式來實現(xiàn)規(guī)則或可替換的過程。策略對象的多個版本表示了完成過程的不同方式。
模式:COMPOSITE

組合模式:將對象組織為樹來表示部分—整體的層次結(jié)構(gòu)。利用COMPOSITE,客戶可以對單獨的對象和對象組合進行同樣的處理。[Gamma et al.1995]
當嵌套容器的關(guān)聯(lián)性沒有在模型中反映出來時,公共行為必然會在層次結(jié)構(gòu)的每一層重復(fù)出現(xiàn),而且嵌套也變得僵化(例如,容器通常不能包含同一層中的其他容器,而且嵌套的層數(shù)也是固定的)??蛻舯仨毻ㄟ^不同的接口來處理層次結(jié)構(gòu)中的不同層,盡管這些層在概念上可能沒有區(qū)別。通過層次結(jié)構(gòu)來遞歸地收集信息也變得非常復(fù)雜。
因此:定義一個把COMPOSITE的所有成員都包含在內(nèi)的抽象類型。在容器上實現(xiàn)那些查詢信息的方法時,這些方法返回由容器內(nèi)容所匯總的信息。而“葉”節(jié)點則基于它們自己的值來實現(xiàn)這些方法??蛻糁恍枋褂贸橄箢愋?,而無需區(qū)分“葉”和容器。
【學(xué)習心得】:很多時候,技術(shù)人員釘子思維是無法區(qū)分技術(shù)角度和模型角度。雖然許多方法是相通的,但不通維度的思考方式也會產(chǎn)生巨大的效果。學(xué)以致用,不是停留在嘴巴上,是在實踐中證明的。
第13章:通過重構(gòu)得到更深層次的理解
通過重構(gòu)得到更深層次的理解是一個涉及很多方面的過程。我們有必要暫停一下,把一些要點歸納到一起。有三件事情是必須要關(guān)注的:
(1)以領(lǐng)域為本;
(2)用一種不同的方式來看待事物;
(3)始終堅持與領(lǐng)域?qū)<覍υ挕?/p>
在尋求理解領(lǐng)域的過程中,可以發(fā)現(xiàn)更廣泛的重構(gòu)機會。但一提到傳統(tǒng)意義上的重構(gòu),我們頭腦中就會出現(xiàn)這樣一幅場景:一兩位開發(fā)人員坐在鍵盤前面,發(fā)現(xiàn)一些代碼可以改進,然后立刻動手修改代碼(當然還要用單元測試來驗證結(jié)果)。這個過程應(yīng)該是一直進行下去,但它并不是重構(gòu)過程的全部。
1、開始重構(gòu)
與傳統(tǒng)重構(gòu)觀點不同的是,即使在代碼看上去很整潔的時候也可能需要重構(gòu),原因是模型的語言沒有與領(lǐng)域?qū)<冶3忠恢拢蛘咝滦枨蟛荒鼙蛔匀坏靥砑拥侥P椭?。重?gòu)的原因也可能來自學(xué)習:當開發(fā)人員通過學(xué)習獲得了更深刻的理解,從而發(fā)現(xiàn)了一個得到更清晰或更有用的模型的機會。
2、探索團隊
不管問題的根源是什么,下一步都是要找到一種能夠使模型表達變得更清楚和更自然的改進方案。這可能只需要做一些簡單、明顯的修改,只需幾小時即可完成。在這種情況下,所做的修改類似于傳統(tǒng)重構(gòu)。但尋找新模型可能需要更多時間,而且需要更多人參與。
修改的發(fā)起者會挑選幾位開發(fā)人員一起工作,這些開發(fā)人員應(yīng)該擅長思考該類問題,了解領(lǐng)域,或者掌握深厚的建模技巧。如果涉及一些難以捉摸的問題,他們還要請一位領(lǐng)域?qū)<壹尤?。想要保證重構(gòu)迭代過程的效率,需要注意幾個關(guān)鍵事項:自主決定,注意范圍和休息,以及練習使用UBIQUITOUS LANGUAGE。
3、借鑒先前的經(jīng)驗
我們沒有必要總?cè)プ鲆恍o謂的重復(fù)工作。用于查找缺失概念或改進模型的頭腦風暴過程具有巨大的作用,通過這個過程可以收集來自各個方面的想法,并把這些想法與已有知識結(jié)合起來。隨著知識消化的不斷開展,就能找到當前問題的答案。
4、針對開發(fā)人員的設(shè)計
軟件不僅僅是為用戶提供的,也是為開發(fā)人員提供的。開發(fā)人員必須把他們編寫的代碼與系統(tǒng)的其他部分集成到一起。在迭代過程中,開發(fā)人員反復(fù)修改代碼。開發(fā)人員應(yīng)該通過重構(gòu)得到更深層的理解,這樣既能夠?qū)崿F(xiàn)柔性設(shè)計,也能夠從這樣一個設(shè)計中獲益。
5、重構(gòu)的時機
如果一直等到完全證明了修改的合理性之后才去修改,那么可能要等待太長時間了。項目正承受巨大的耗支,推遲修改將使修改變得更難執(zhí)行,因為要修改的代碼已經(jīng)變得更加復(fù)雜,并更深地嵌入到其他代碼中。持續(xù)重構(gòu)漸漸被認為是一種“最佳實踐”,但大不部分團隊仍然對它抱有很大的戒心。
在探索領(lǐng)域的過程中,在培訓(xùn)開發(fā)人員的過程中,以及在開發(fā)人員與領(lǐng)域?qū)<疫M行思想交流的過程中,必須始終堅持把“通過重構(gòu)得到更深層次理解”作為這些工作的一部分。因此,當發(fā)生一下情況時,就應(yīng)該進行重構(gòu)了:
□ 設(shè)計沒有表達出團隊對領(lǐng)域的最新理解;
□ 重要的概念被隱藏在設(shè)計中了(而且你已經(jīng)發(fā)現(xiàn)了把它們呈現(xiàn)出來的方法);
□ 發(fā)現(xiàn)了一個能令某個重要的設(shè)計部分變得更靈活的機會。
6、危機就是機遇
傳統(tǒng)意義上的重構(gòu)聽起來是一個非常穩(wěn)定的過程。但通過重構(gòu)得到更深層理解往往不是這樣的。在對模型進行一段時間穩(wěn)定的改進后,你可能突然有所頓悟,而這會改變模型中的一切。這些突破不會每天都發(fā)生,然而很大一部分深層模型和柔性設(shè)計都來自這些突破。
這樣的情況往往看起來不像是機遇,而更像危機。例如,你突然發(fā)現(xiàn)模型中一些明顯的缺陷,在表達方面顯示出一個很大的漏洞,或存在一些沒有表達清楚的關(guān)鍵區(qū)域。或者有些描述是完全錯誤的。這些都表明團隊對模型的理解已經(jīng)達到了一個新的水平。他們現(xiàn)在站在更高的層次上發(fā)現(xiàn)了原有模型的弱點。他們可以從這種角度構(gòu)思一個更好的模型。
【學(xué)習心得】:我曾幾何時一直認為,發(fā)現(xiàn)自己問題是一種恥辱。這種思維極其可怕,當我不再發(fā)現(xiàn)自己問題的時候,那才叫可怕。在軟件領(lǐng)域,新思維的提升叫重構(gòu),在生活方面,新觀念的形成叫重生。