需求凍結
敏捷方法和傳統方法的區別在于對待變化的態度。傳統的做法是在編碼活動開始之前進行充分、細致的需求調研和設計工作,并簽署合同。確保所有的前期工作都已經完成之后,才開始編碼、測試等工作。如果發生需求變化的情況,則需要進行嚴格的控制。而在現實中,這種方法往往會由于對開發人員和客戶雙方需求理解的不一致,需求本身的變化性等問題而導致項目前期就完全固化需求變得不現實。結果要么是拒絕需求的改變而令客戶的利益受損,要么是屈從于需求的改變而導致項目失控。敏捷方法則不同,它強調擁抱變化。對于易變的需求,它使用了一系列實踐,來馴服這只烈馬。其核心則是迭代式開發。應該承認,做到掌握需求并不是一件容易的事,而迭代開發也很容易給開發團隊帶來額外的高昂成本。要做到這一點,需要有其它實踐的配合(下文會提到)。因此,我們在迭代開發進入到一定的階段的時候,需要進行需求凍結。這時候的需求凍結和上面提到的一開始就固化需求是不一樣的。首先,用戶經歷過一次或幾次的迭代之后,對軟件開發已經有了形象的認識,對需求不再是霧里看花。其次,通過利用原型法等實踐,用戶甚至可能對軟件的最終形式已經有了一定的經驗。這樣,用戶提出的需求基本上可以代表他們的真實需求。即便還有修改,也不會對軟件的架構產生惡劣的影響。最后,需求凍結的時點往往處于項目的中期,這時候需求如果仍然不穩定,項目的最后成功就難以得到保證。
在需求凍結之前,不要過分的把精力投入到文檔的制作上,正確的做法是保留應有的信息,以便在稍后的過程中完成文檔,而不是在需求未確定的時候就要求格式精美的文檔。在格式化文檔上很容易就會花費大量的時間,如果需求發生改變,所有的投入都浪費了。文檔的投入量應該隨著項目的進行而增大。但這決不是說文檔不重要,因為你必須要保留足夠的信息,來保證文檔能夠順利的創建。
確保有專人來接受對變更需求的請求,這樣可以確保需求的變化能夠得以控制。這項工作可以由項目經理(或同類角色)負責,也可以由下文所說的變更委員會負責。小的、零散的需求很容易對開發人員產生影響,而他們有更重要的任務――把項目往前推進。此時項目經理就像是一個緩沖區,由他來決定需求的分類、優先級、工作量、對現有軟件的影響程度等因素,從而安排需求變更的計劃――是在本次迭代中完成,還是在下一次迭代中完成。
建立需求變更委員會是一種很好的實踐,它由項目的不同類型的涉眾組成,可能包括管理、開發、客戶、文檔、質量保證等方面的人員。他們對需求變更做出評估及決定,評估需求對費用、進度、及各方面的影響,并做出是否以及如何接受需求的決定。由于委員會往往涉及到整個項目團隊,因此效率可能會成為它的主要缺點。在這種情況下,一方面可以加強委員會的管理,一方面可以保證委員會只處理較大的需求變更。
在項目的不同時候都需要對需求進行不同程度的約束,這聽起來和我們提倡的擁抱變化有些矛盾。其實不然。對需求進行約束的主要目的是防止需求的膨脹和蔓延,避免不切實際的功能列表。我們常常能夠提到諸如 "這項功能很酷,我們的軟件也要包含它"以及"我們的對手已經開發出這項功能了,最終的軟件必須要包含這項功能"之類的話語。這就是典型的需求蔓延的征兆。在項目開始時正確的估計功能進度,在項目中期時控制需求,在項目晚期是杜絕新增需求,甚至剪切現有需求。通過三種方法來保證軟件能夠保時保質的推出。
穩定架構
即便是需求已經成功的凍結了,我們仍然面對一個不夠穩定的架構。這是必然的,而不穩定的程度則和團隊的能力,以及對目標領域的理解程度成反比。因此,架構也需要改進。前一個模式中,我們討論了對架構的重構,其實這就是令架構穩定的一種方法。經驗數據表明,一系列小的變化要比一次大變化容易實現,也更容易控制。因此在迭代中對架構進行不斷重構的做法乍看起來會托慢進度,但是它為軟件架構的穩定奠定了基礎。重構講究兩頂帽子的思維方式,即這一個時段進行功能上的增加,下一個時段則進行結構的調整,兩個時段決不重復,在對增加功能時不考慮結構的改進,在改進結構時也同樣不考慮功能的增加。而在架構進行到穩定化這樣一個階段之后,其主要的職責也將變為對結構的改進了。從自身的經驗來看,這個階段是非常重要的,對軟件質量的提高,對加深項目成員對目標領域的認識都有莫大的幫助。而這個階段,也是很容易提煉出通用架構,以便軟件組織進行知識積累的。
在這個階段中,讓有經驗的架構師或是高級程序員介入開發過程是非常好的做法。這種做法來自于軟件評審的實踐。無論是對于改進軟件質量,還是提高項目成員素質,它都是很有幫助的。
架構穩定的實踐中暗含了一個開發方法的穩定。程序員往往喜歡新的技術、新的工具。這一點無可厚非。但是在項目中,采用新技術和新工具總是有風險的?赡軓S商推出的工具存在一些問題沒有解決,或者該項技術對原有版本的支持并不十分好。這些都會對項目產生不良的影響。因此,如果必須在項目中采用新技術和新工具的話,有必要在項目初期就安排對新事物進行熟悉的時間。而在架構進入穩定之前,工具的用法、技術的方法都必須已經完成試驗,已經向所有成員推廣完畢。否則必須要在延長時間和放棄使用新事物之間做一個權衡。
保證架構穩定的優秀實踐
在文章的開頭,我們就談到說在項目起始階段就制定出準確、詳細的架構設計是不太現實的。因此,敏捷方法中有很多的實踐來令最初的架構設計穩定化。實際上,這些實踐并非完全是敏捷方法提出的新概念。敏捷方法只是把這些比較優秀的實踐組織起來,為穩定的架構設計提供了保證。以下我們就詳細討論這些實踐。
在不穩定的環境中尋求穩定因素。什么是穩定的,什么是不穩定的。RUP推薦使用業務實體(Business Entity)法進行架構設計。這種方法的好處之一是通過識別業務實體從而建立起來的架構是相對穩定的。因為業務實體在不穩定的業務邏輯中屬于穩定的元素。大家可以想象,公司、雇員、部門這些概念,幾十年來并沒有太大的變化。對于特定的業務也是一樣的。例如對于會計總帳系統來說,科目、余額、分戶賬、原始憑證,這些概念從來就沒有太大的變化,其對應的行為也相差不大。但是某些業務邏輯就完全相反了。不同的系統業務邏輯不同,不同的時點業務邏輯也有可能發生變化。例如,對于不同的制造業來說,其成本核算的邏輯大部分都是不一樣的。即便行業相同,該業務邏輯也沒有什么共性。因此,穩定的架構設計應該依賴于穩定的基礎,對于不穩定的因素,較好的做法是對其進行抽象,抽象出穩定的東西,并且把不穩定的因素封裝在單獨的位置,避免其對其它模塊的影響。而這種思路的直接成果,就是下一段提到的針對接口編程的做法。例如對于上面提到的成本核算來說,雖然它們是易變的、不穩定的,但是它們仍然存在穩定的東西,就是大部分制造業企業都需要成本核算。這一點就非常的重要,因此著意味著接口方法是相對固定的。
保持架構穩定性的另一種方法是堅持面向接口編程的設計方法。我們在分層模式中就提到了面向接口編程的設計方法,鼓勵抽象思維、概念思維。從分層模式中提到的示例中(詳見分層模式下篇的面向接口編程一節),我們可以看出,接口的一大作用是能夠有效的對類功能進行分組,從而避免客戶程序員了解和他不相關的知識。設計模式中非常強調接口和實現分離,其主要的表現形式也正是如此,客戶程序員不需要知道具體的實現,對他們來說,只需要清楚接口發布出的方法就可以了。
從另一個方面來看,之所以要把接口和實現相分離,是因為接口是需求中比較穩定的部分,而實現則是和具體的環境相關聯的。下圖為Java中Collection接口公布出的方法?梢钥吹,在這個層次上,Collection接口只是根據容器的特性定義了一些穩定的方法。例如增加、刪除、比較運算等。所以這個接口是相對比較穩定的,但是對于具體的實現類來說,這些方法的實現細節都有所差別。例如,對于一個List和一個Array,它們對于增加、刪除的實現都是不一樣的。但是對于客戶程序員來說,除非有了解底層實現的需要,否則他們不用了解List的add方法和Array的add方法有什么不同。另一方面,將這些方法實現為固定的、通用的接口,也有利于接口的開發者。他們可以將實現和接口相分離,此外,只要滿足這些公布的接口,其它軟件開發團隊同樣能夠開發出合用的應用來。在當前這樣一個講求合作、講求效率的大環境中。這種開發方法是非常重要的。
java.util Interface Collection | |
boolean | add(Object o) |
boolean | addAll(Collection c) |
void | clear() |
boolean | contains(Object o) |
boolean | containsAll(Collection c) |
boolean | equals(Object o) |
int | hashCode() |
boolean | isEmpty() |
Iterator | iterator() |
boolean | remove(Object o) |
boolean | removeAll(Collection c) |
boolean | retainAll(Collection c) |
int | size() |
Object[] | toArray() |
Object[] | toArray(Object[] a) |
代碼重構是令架構趨于穩定的另一項方法。準確而言,重構應該是程序員的一種個人素質。但是在實際中,我們發現,重構這種行為更加適合作為開發團隊的共同行為。為什么這么說呢?最早接觸重構概念的時候,我對面向對象的認識并不深入。因此對重構的概念并不感冒。但隨著經驗的積累,面向對象思維的深入。我漸漸發現,重構是一種非常優秀的代碼改進方式,它通過把原子性的操作,逐步的改進代碼質量,從而達到改進軟件架構的效果。當程序員逐漸熟練運用重構的時候,他已經不再拘泥于這些原子操作,而是自然而然的寫出優秀的軟件。這是重構方法對各人行為的改進。另一方面,對于一個團隊來說,每個人的編程水平和經驗都不一而足,因此軟件的各個模塊質量也都是參差不齊的。這種情況下,軟件質量的改進就已經不是個人的問題了,而該問題的難度要比前一個問題大的多。此時重構方法更能夠發揮其威力。在團隊中提倡使用、甚至半強制性使用重構,有助于分享優秀的軟件設計思路,提高軟件的整體架構。而此時的重構也不僅僅局限在代碼的改進上(指的是Martin Fowler在重構一書中提到的各種方法),還涉及到分析模式、設計模式、優秀實踐的應用上。同時,我們還應該看到,重構還需要其它優秀實踐的配合。例如代碼復審和測試優先。
總結
令架構趨于穩定的因素包括令需求凍結和架構改進兩個方面。需求凍結是前提,架構改進是必然的步驟。在面向對象領域,我們可以通過一些實踐技巧來保持一個穩定的架構。這些實踐技巧體現在從需求分析到編碼的過程中。穩定化模式和下一篇的代碼驗證模式有很多的關聯,細節問題我們會在下一篇中討論。
(待續)
作者簡介:
林星,辰訊軟件工作室項目管理組資深項目經理,有多年項目實施經驗。辰訊軟件工作室致力于先進軟件思想、軟件技術的應用,主要的研究方向在于軟件過程思想、Linux集群技術、OO技術和軟件工廠模式。您可以通過電子郵件 iamlinx@21cn.com 和他聯系。
文章來源于領測軟件測試網 http://www.kjueaiud.com/