面向?qū)ο筌浖_發(fā)的敏捷過程(一)
面向?qū)ο筌浖_發(fā)的敏捷過程
軟件開發(fā)的復(fù)雜性 :
計(jì)算機(jī)硬件界的摩爾定律(每隔 18 個(gè)月計(jì)算機(jī)硬件的運(yùn)算速度提高一倍,價(jià)格下降一半)
適用于硬件的發(fā)展規(guī)律已經(jīng)超過三十年了。人們想當(dāng)然的認(rèn)為計(jì)算機(jī)軟件的發(fā)展速度和硬件的發(fā)展速度相當(dāng),但是不幸的是:每次重大的硬件升級(jí)之后,隨著更大功能更豐富的軟件的出現(xiàn),硬件的潛能再一次被無情的榨取殆盡。許多開發(fā)的軟件系統(tǒng)不斷的遭受進(jìn)度延期,人員資金和時(shí)間等預(yù)算無休止的增加,軟件質(zhì)量的不斷反復(fù),開發(fā)出來的系統(tǒng)對(duì)客戶的新需求響應(yīng)緩慢,更改困難的噩夢(mèng)。
這樣的現(xiàn)實(shí)是由軟件的固有復(fù)雜性造成的,軟件不同于硬件的生產(chǎn)過程,是由人的智力勞動(dòng)完成人的需求到機(jī)器程序的翻譯轉(zhuǎn)換過程。需求可能不清晰,對(duì)需求可能出現(xiàn)個(gè)人理解上的差異,選擇實(shí)現(xiàn)方法的差異,需求的不斷變化,具體實(shí)現(xiàn)語言平臺(tái)的差異,軟件生產(chǎn)中采用的過程,具體實(shí)現(xiàn)人員的變動(dòng)等等都會(huì)對(duì)最終的產(chǎn)品產(chǎn)生影響。想象一下,如果一種變化的因素只有兩種可能,那么可以使用簡(jiǎn)單的 0 , 1 表示,只有 10 個(gè)變化因素的組合就已經(jīng)達(dá)到了 2--10=1024 種可能性,而實(shí)際開發(fā)中變化的因素輕易就超過 10 個(gè)以上,每個(gè)變化的可能是還不止兩個(gè),因此軟件的復(fù)雜性很快就會(huì)超出人的理解程度。有一句經(jīng)典的軟件開發(fā)名言:世界上唯一不變的是變化本身。不斷出現(xiàn)的變化,會(huì)使初始的設(shè)計(jì)和最終的需求之間的距離越來越遠(yuǎn)。
軟件的臭味:
軟件開發(fā),使用,維護(hù)中出現(xiàn)了以下的“臭味”:
僵化性: rigidity 很難對(duì)系統(tǒng)進(jìn)行改動(dòng),因?yàn)槊總€(gè)改動(dòng)都會(huì)迫使許多對(duì)系統(tǒng)其他部分的其他改動(dòng)。即使是簡(jiǎn)單的改動(dòng),也會(huì)迫使導(dǎo)致右依賴關(guān)系的模塊的連鎖改動(dòng)。
脆弱性: fragility 對(duì)系統(tǒng)的改動(dòng)會(huì)導(dǎo)致系統(tǒng)中和改動(dòng)的地方在概念上無關(guān)的許多地方出現(xiàn)問題。出現(xiàn)新問題的地方和改動(dòng)的地方?jīng)]有概念上的關(guān)聯(lián),難以排錯(cuò),排錯(cuò)的過程中又會(huì)引入更多的“臭蟲”。
牢固性 immobility 很難解開系統(tǒng)的糾結(jié),使它成為其他系統(tǒng)中重用的組件。系統(tǒng)中包含了對(duì)其他系統(tǒng)中有用的功能,當(dāng)其他人想復(fù)用這個(gè)功能到新的系統(tǒng)時(shí),剝離出獨(dú)立的組件的難度遠(yuǎn)遠(yuǎn)大于重新實(shí)現(xiàn)的難度,在時(shí)間和進(jìn)度的壓力下,大多數(shù)人只有選擇拷貝涂鴉的方式來實(shí)現(xiàn)新系統(tǒng)的功能。
粘滯性: viscosity 做正確的事情比錯(cuò)誤的事情要困難。程序完成正常的功能總是傾向于得到不正確的結(jié)果。
不必要的復(fù)雜性: needless complexity 設(shè)計(jì)中包含有不具有任何直接好處的基礎(chǔ)結(jié)構(gòu)。為了預(yù)防后期維護(hù)更改需求的對(duì)源碼的修改,在設(shè)計(jì)之初放置了那些處理潛在變化的代碼來保持軟件的靈活性,這樣的結(jié)果是軟件中包含了很多復(fù)雜的結(jié)構(gòu),理解起來更加困難。
不必要的重復(fù): needless repetition 設(shè)計(jì)中包含有重復(fù)的結(jié)構(gòu),而該重復(fù)的結(jié)構(gòu)可以使用單一的抽象進(jìn)行統(tǒng)一。對(duì)鼠標(biāo)右鍵(剪切,復(fù)制,粘貼)的濫用,使得完成同一或類似的代碼片斷出現(xiàn)在系統(tǒng)各處。如果原始的代碼段完成的功能需要變化,或者存在錯(cuò)誤,排錯(cuò)和增加新的功能變得非常困難。
晦澀性: opacity 很難閱讀,理解。沒有很好的表現(xiàn)出意圖。
以上討論了系統(tǒng)構(gòu)架的臭味,下面討論微觀層次上代碼的臭味:
代碼的臭味
重復(fù)代碼:重復(fù)的代碼使得更改功能和排錯(cuò)更加困難。同樣的模塊錯(cuò)誤會(huì)在拷貝粘貼的程序各處多次出現(xiàn)。
過長(zhǎng)的函數(shù):程序越長(zhǎng)越難于理解,這已經(jīng)是軟件業(yè)開發(fā)的常識(shí)。越難理解的程序,使用維護(hù)的成本就越大。如果一個(gè)函數(shù)的行數(shù)超過一頁,很少有人能夠在看到下一頁的時(shí)候還清楚的記得函數(shù)開頭的變量定義,理解和查錯(cuò)更加困難。
過大類:在一個(gè)類中完成幾乎所有需要的功能。十項(xiàng)全能的人是不存在的,軟件也一樣。
過長(zhǎng)的參數(shù)列:如果一個(gè)函數(shù)(方法)的調(diào)用參數(shù)過長(zhǎng),使用這個(gè)函數(shù)的調(diào)用過程也一定是困難的。想象一下,調(diào)用一個(gè)十個(gè)以上參數(shù)存儲(chǔ)過程會(huì)有多么痛苦。這還只是開始,如果任一個(gè)參數(shù)的定義(名稱,類型)發(fā)生輕微的變化,函數(shù)的調(diào)用客戶端會(huì)有多么大的改動(dòng)。
其他的臭味還有發(fā)散式變化,散彈槍修改,依戀情結(jié),數(shù)據(jù)泥團(tuán),基本型別偏執(zhí),復(fù)雜的 switch 分支語句,平行的繼承體系,冗贅類,夸夸其談的未來性,令人迷惑的暫時(shí)值域,過度耦合的消息鏈,中間轉(zhuǎn)手人,狎昵關(guān)系,異曲同工的類,不完美的程序庫(kù)類,純數(shù)據(jù)類(數(shù)據(jù)啞元),子類不需要父類的某些特性,過多注釋。詳細(xì)的討論可以參見《重構(gòu)》的介紹。
面向?qū)ο筌浖O(shè)計(jì)的原則 :
總體原則 :
1.? 針對(duì)于接口(抽象)編程,而不要針對(duì)于實(shí)現(xiàn)(具體)編程。
舉例來說:操作系統(tǒng)是對(duì)邏輯計(jì)算機(jī)的抽象,通過操作系統(tǒng)的抽象我們不需要考慮具體使用的硬件配置,可以在較高的層次上進(jìn)行更高生產(chǎn)力的應(yīng)用。再如:匯編語言對(duì)機(jī)器的 0 , 1 代碼進(jìn)行了抽象,大大加快了開發(fā)效率,后來使用的高級(jí)語言和第四代語言模型驅(qū)動(dòng)抽象的級(jí)別更高,生產(chǎn)力也更高。再如: java 和 .net 實(shí)現(xiàn)于一個(gè)抽象的軟件虛擬機(jī),進(jìn)一步使開發(fā)出來的組件可以跨平臺(tái)和操作系統(tǒng)。通過抽象出數(shù)據(jù)訪問層(持久化層),可以使業(yè)務(wù)邏輯和具體的數(shù)據(jù)庫(kù)訪問代碼分離,更換數(shù)據(jù)庫(kù)提供商對(duì)已有的組件沒有影響。具體實(shí)現(xiàn)可以參照 hibernate 實(shí)現(xiàn)和 dao (數(shù)據(jù)訪問對(duì)象)模式。
優(yōu)勢(shì): 1 )降低程序各個(gè)部分之間的耦合性,使程序模塊互換成為可能。調(diào)用的客戶端無需知道具體使用的對(duì)象類型,只要對(duì)象有客戶希望的接口就可以使用,對(duì)象具體是如何實(shí)現(xiàn)這些接口的,客戶并不需要考慮。 2 )簡(jiǎn)化了程序各個(gè)部分的單元測(cè)試,將需要測(cè)試的程序模塊中通過重構(gòu)提煉出抽象的接口,然后編制和接口一致的 Mock 類,測(cè)試就會(huì)變得很容易。如果應(yīng)用了測(cè)試優(yōu)先的方法,從簡(jiǎn)化客戶端調(diào)用的角度,還可以經(jīng)過抽象改善軟件模塊的設(shè)計(jì)。 3 )模塊的部署升級(jí)由于模塊之間的耦合度降低變得更加容易。
相關(guān)的設(shè)計(jì)模式有創(chuàng)建型模式中的工廠模式,結(jié)構(gòu)型模式中的代理模式和組合模式等。
2.? 對(duì)象組合優(yōu)于類繼承。
面向?qū)ο鬄檐浖_發(fā)引入了三大工具:繼承,多態(tài)和重載。繼承使得程序員可以快速的通過擴(kuò)展子類來增加功能,但是由于繼承是在編譯時(shí)確定的,因此增加的功能較多時(shí),繼承不夠靈活,還有可能出現(xiàn)“子類爆炸”的局面(為了完成新添功能,不得不在繼承體系中添入大量的之間只有細(xì)微差別的子類,掌握使用擴(kuò)展都會(huì)變得非常困難)。而通過對(duì)象的組合,可以動(dòng)態(tài)透明的添加功能。相關(guān)設(shè)計(jì)模式有裝飾模式和代理模式等。
3.? 分離變化。前面說過需求是在不斷變化的,不同的變化可能是倉(cāng)庫(kù)安全庫(kù)存的計(jì)算方法,可能是報(bào)表和數(shù)據(jù)的展現(xiàn)形式,通過把這些不同的變化識(shí)別并分離出來不同的對(duì)象委托,簡(jiǎn)化了客戶端的調(diào)用和升級(jí)。相關(guān)的設(shè)計(jì)模式有命令模式,觀察者模式,策略模式,狀態(tài)模式,模版方法模式等。
具體原則:
1.? 單一職責(zé)原則 srp ( single responsibility principle ):一個(gè)模塊的功能應(yīng)該盡可能的內(nèi)聚。如果一個(gè)類發(fā)生了變化,引起變化的原因應(yīng)該有且只有一個(gè)。每一個(gè)類承擔(dān)的職責(zé)都是一個(gè)變化的軸線,需求變化時(shí),會(huì)體現(xiàn)為類的職責(zé)的變化。如果一個(gè)類承擔(dān)的職責(zé)過多,就等于把這些職責(zé)耦合在了一起,一個(gè)職責(zé)的變化會(huì)影響這個(gè)類完成其他職責(zé)的能力,會(huì)出現(xiàn)前面所說的軟件的臭味之一脆弱性。相關(guān)的設(shè)計(jì)模式有
2.? 開放封閉原則 ocp ( open closed principle ):一個(gè)模塊應(yīng)該對(duì)功能的擴(kuò)展開放,支持新的行為,對(duì)自身的更改封閉。每次對(duì)模塊的修改都可能會(huì)引入新的錯(cuò)誤和新的依賴。因此擴(kuò)展新功能時(shí),已經(jīng)編好的模塊源碼和二進(jìn)制代碼都是不應(yīng)該修改的。相關(guān)的設(shè)計(jì)模式有適配器模式,橋接模式,訪問者模式等。
3.?
Liskov
替換原則
lsp
(
liskov subtitle principle
)子類型必須可以替換掉他的基類型。一個(gè)基類的多個(gè)子類型之間要完成動(dòng)態(tài)的替換,各個(gè)子類型必須都可以被他們的基類型替換,這樣他們之間動(dòng)態(tài)替換后,客戶端調(diào)用的代碼就不需要冗贅的
switch
類型判斷代碼。如果
子類型無法替換基類型,將會(huì)導(dǎo)致在派生類對(duì)象作為基類對(duì)象進(jìn)行傳值時(shí)的錯(cuò)誤。這樣多態(tài)機(jī)制處于癱瘓狀態(tài)了。相關(guān)設(shè)計(jì)模式為組合模式。
4.? 依賴倒置原則 dip ( dependent inverse principle )高層模塊不應(yīng)該依賴于底層模塊,抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴于抽象。假定所有的具體類都是回變化的,因此如果一個(gè)客戶端依賴于(調(diào)用或聲明)具體的類型,那么當(dāng)這個(gè)具體的類型變化時(shí),依賴的客戶端業(yè)必須同時(shí)進(jìn)行修改。這些具體的更改可能出現(xiàn)在使用了某個(gè)特定的網(wǎng)絡(luò)協(xié)議,特殊的系統(tǒng) api 調(diào)用,特定的數(shù)據(jù)庫(kù)儲(chǔ)存過程等,這些用法或多或少都會(huì)使客戶端調(diào)用和具體類型成為鐵板一塊,比較難于重用。 Java 社區(qū)中比較熱門的 j2ee 輕量級(jí)容器框架 spring 就很好的實(shí)現(xiàn)了本原則。
5 .接口隔離原則 isp ( interface segregation principle )不應(yīng)該強(qiáng)迫客戶依賴于它們不使用的方法。因?yàn)槊恳粋€(gè)實(shí)現(xiàn)接口的對(duì)象必須實(shí)現(xiàn)所有接口中定義的方法。如果接口的粒度比較小,實(shí)現(xiàn)接口的對(duì)象可以使用一種即用即付的方式動(dòng)態(tài)實(shí)現(xiàn)接口。每個(gè)接口的粒度很小,復(fù)用起來也非常容易。
這體現(xiàn)了一個(gè)趨勢(shì):為了更好的實(shí)現(xiàn)重用,接口,函數(shù),模塊和類等傾向于更容易使用的“小”體積。
敏捷軟件開發(fā)的宣言和實(shí)踐:
軟件開發(fā)項(xiàng)目的失敗使得人們開始思考軟件開發(fā)的過程,人們希望通過引入嚴(yán)格的過程控制產(chǎn)生軟件生命周期中各個(gè)階段的文檔和制品來保證軟件的質(zhì)量。比較出名的業(yè)界實(shí)施方法論有 cmmi (能力成熟度模型)和 rup (瑞理統(tǒng)一過程),這些方法論都是重型的。舉例來說,沒有經(jīng)過剪裁的 Rup 實(shí)現(xiàn)起來,至少需要在全周期完成四十個(gè)以上的制品文檔,文檔的編寫維護(hù)和源代碼的同步等需要非常多的資源,十人以下的開發(fā)團(tuán)隊(duì)一般沒有精力、時(shí)間、人員配置完成這些制品。失控的過程的膨脹迫使人們尋找一種快速工作,相應(yīng)變化的“敏捷的”方法。敏捷團(tuán)隊(duì)提倡通過團(tuán)隊(duì)成員之間充分有效的溝通統(tǒng)一大家的目標(biāo),結(jié)伴的方式完成開發(fā)技術(shù)的團(tuán)隊(duì)內(nèi)傳承,使用“夠用就好”的輕量甚至免費(fèi)的工具管理過程。可以正常工作的代碼擺在首要地位,只有必要的時(shí)候才生產(chǎn)必要的文檔。強(qiáng)調(diào)和客戶面對(duì)面地交流合作,積極地響應(yīng)客戶需求的變化而不是遵循機(jī)械的計(jì)劃。使用較短的迭代周期,近早和持續(xù)提交有價(jià)值的軟件給客戶來驗(yàn)證并修正和用戶需求的吻合程度。提倡可以持續(xù)的穩(wěn)定的開發(fā)節(jié)奏,長(zhǎng)期“小步快走”的方式代替突然的“百米沖刺”。保持設(shè)計(jì)最優(yōu),最簡(jiǎn)單的設(shè)計(jì)并且持續(xù)改進(jìn),不斷調(diào)整。
posted on 2006-08-15 16:54 amigojava 閱讀(1576) 評(píng)論(1) 編輯 收藏