高效程序员的45个习惯
生活随笔
收集整理的這篇文章主要介紹了
高效程序员的45个习惯
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
本文轉(zhuǎn)載 《高效程序員的45個(gè)習(xí)慣》一書(shū)目錄
第1章 敏捷---高效軟件開(kāi)發(fā)之道
第2章 態(tài)度決定一切
1.做事
2.欲速則不達(dá)
3.對(duì)事不對(duì)人
4.排除萬(wàn)難,奮勇前進(jìn)
第3章 學(xué)無(wú)止境
5.跟蹤變化
6.對(duì)團(tuán)隊(duì)投資
7.懂得丟棄
8.打破沙鍋問(wèn)到底
9.把握開(kāi)發(fā)節(jié)奏
第4章交付用戶想要的軟件
10.讓客戶做決定
11.讓設(shè)計(jì)指導(dǎo)而不是操縱開(kāi)發(fā)
12.合理地使用技術(shù)
13.保持可以發(fā)布
14.提早集成,頻繁集成
15.提早實(shí)現(xiàn)自動(dòng)化部署
16.使用演示獲得頻繁反饋
17.使用短迭代,增量發(fā)布
18.固定的價(jià)格就意味著背叛承諾
第5章 敏捷反饋
19.守護(hù)天使
20.先用它再實(shí)現(xiàn)它
21.不同環(huán)境,就有不同問(wèn)題
22.自動(dòng)驗(yàn)收測(cè)試
23.度量真實(shí)的進(jìn)度
24.傾聽(tīng)用戶的聲音
第6章 敏捷編碼
25.代碼要清晰地表達(dá)意圖
26.用代碼溝通
27.動(dòng)態(tài)評(píng)估取舍
28.增量式編程
29.保持簡(jiǎn)單
30.編寫(xiě)內(nèi)聚的代碼
31.告知,不要詢問(wèn)
32.根據(jù)契約進(jìn)行替換
第7章 敏捷調(diào)試
33.紀(jì)錄問(wèn)題解決日志
34.警告就是錯(cuò)誤
35.對(duì)問(wèn)題各個(gè)擊破
36.報(bào)告所有的異常
第一章 敏捷--高效軟件開(kāi)發(fā)之道不管路走了多遠(yuǎn),錯(cuò)了就要重新返回.------土耳其諺這句土耳其諺語(yǔ)的含義顯而易見(jiàn),你也會(huì)認(rèn)同這是軟件開(kāi)發(fā)應(yīng)該遵守的原則.但很多時(shí)候,開(kāi)發(fā)人員(包括我們自己)發(fā)現(xiàn)自己走錯(cuò)路后,
卻不愿意立即回頭,而是抱著遲早會(huì)步入正規(guī)的僥幸心理,繼續(xù)錯(cuò)下去.人們會(huì)想,或許差不多少吧,或許錯(cuò)誤不像想象的那么嚴(yán)重.假使開(kāi)發(fā)
軟件是個(gè)確定的,線性的過(guò)程,我們隨時(shí)可以撤回來(lái),如同諺語(yǔ)中所說(shuō)的那樣.然而,它卻不是.相反,軟件開(kāi)發(fā)更像是在沖浪----一直處于動(dòng)態(tài)、不斷變化的環(huán)境中.大海本身無(wú)法預(yù)知,充滿風(fēng)險(xiǎn),并且海里還可能有鯊魚(yú)出沒(méi).沖浪之所以如此有挑戰(zhàn)性,是因?yàn)椴ɡ烁鞑煌?在沖浪現(xiàn)場(chǎng),每次波浪都是獨(dú)一無(wú)二的,沖浪的動(dòng)作也會(huì)各不相同.例如,海灘邊的波浪和峭
壁下的海浪就是很大的區(qū)別.在軟件開(kāi)發(fā)領(lǐng)域,在項(xiàng)目研發(fā)過(guò)程中出現(xiàn)的需求變化,像波浪一樣.在不同的業(yè)務(wù)領(lǐng)域和應(yīng)用下,軟件項(xiàng)目具有不同的形式,帶來(lái)了不同的
挑戰(zhàn).甚至還有鯊魚(yú)以各種偽裝出沒(méi).
軟件項(xiàng)目的成敗,依賴與整個(gè)項(xiàng)目團(tuán)隊(duì)中所有開(kāi)發(fā)成員的技術(shù)水平,對(duì)于他們的培訓(xùn),以及他們各自的能力高低.就像成功的沖浪手一樣,
開(kāi)發(fā)人員必須也是技術(shù)扎實(shí)、懂得掌握平衡和能夠敏捷行事的人.不管是預(yù)科之外的波浪沖擊,還是預(yù)想不到的設(shè)計(jì)失敗,在這兩種情況下敏
捷都意味著可以快速地適應(yīng)變化.
敏捷的精神那么,到底什么是敏捷開(kāi)發(fā)方法?整個(gè)敏捷開(kāi)發(fā)方法的運(yùn)動(dòng)從何而來(lái)呢?2001年2月,17位志愿者(包括作者之一Andy在內(nèi))聚集在美國(guó)猶他州雪鳥(niǎo)度假勝地,討論一個(gè)新的軟件開(kāi)發(fā)趨勢(shì),這個(gè)趨勢(shì)被不嚴(yán)格地成為"輕量型軟件開(kāi)發(fā)過(guò)程".
我們都見(jiàn)過(guò)了因?yàn)殚_(kāi)發(fā)過(guò)程的冗余、笨重、繁雜而失敗的項(xiàng)目.世上應(yīng)該有一種更好的軟件開(kāi)發(fā)方法---只關(guān)注真正重要的情況,少關(guān)注那些占用大量時(shí)間而無(wú)甚裨益的不重要的情況.
這些志愿者們給這個(gè)方法學(xué)取名為敏捷.他們審視了這種新的軟件開(kāi)發(fā)方法,并且發(fā)布了敏捷開(kāi)發(fā)的宣言:一種把為人為本、團(tuán)隊(duì)合作、快速響應(yīng)變化和可工作的軟件作為宗旨的開(kāi)發(fā)方法(本頁(yè)最開(kāi)始的方框就是宣言的內(nèi)容).
敏捷開(kāi)發(fā)可以快速地響應(yīng)變化,它強(qiáng)調(diào)團(tuán)隊(duì)合作,人們專注于具體可行的目標(biāo)(實(shí)現(xiàn)真正可以工作的軟件),這就是敏捷的精神.它打破了那種基于計(jì)劃的瀑布式軟件開(kāi)發(fā)方法,將軟件開(kāi)發(fā)的實(shí)際重點(diǎn)轉(zhuǎn)移到一種更加自然和可持續(xù)的開(kāi)發(fā)方式上.
它要求團(tuán)隊(duì)中的每一個(gè)人(包括與團(tuán)隊(duì)合作的人)都具備職業(yè)精神,并積極地期望項(xiàng)目能夠獲得成功.它并不要求所有人都是有經(jīng)驗(yàn)的專業(yè)人員,但必須具有專業(yè)的工作態(tài)度----每個(gè)人都希望盡最大可能做好自己的工作.
如果在團(tuán)隊(duì)中經(jīng)常有人曠工、偷懶甚至直接怠工,那么這樣的方法并不適合你,你需要的是一些重量級(jí)的、緩慢的、低生產(chǎn)率的開(kāi)發(fā)方法.如果情況并非如此,你就可能用敏捷的方式進(jìn)行開(kāi)發(fā).
這意味著你不會(huì)在項(xiàng)目結(jié)束的時(shí)候才開(kāi)始測(cè)試,不會(huì)在月底才進(jìn)行一次系統(tǒng)集成,也不會(huì)在一開(kāi)始編碼的時(shí)候就停止收集需求和反饋.
相反,這些活動(dòng)會(huì)貫穿項(xiàng)目的整個(gè)生命
周期.事實(shí)上,只要有人繼續(xù)使用這個(gè)軟件,
開(kāi)發(fā)就沒(méi)有真正結(jié)束.我們進(jìn)行的是持續(xù)開(kāi)
發(fā)、持續(xù)反饋.你不需要等到好幾個(gè)月之后才發(fā)現(xiàn)問(wèn)題:越早發(fā)現(xiàn)問(wèn)題,就越容易修復(fù)問(wèn)題,所以應(yīng)該就在此時(shí)此刻把問(wèn)題修復(fù).
這就是敏捷的重點(diǎn)所在
這種持續(xù)前進(jìn)的開(kāi)發(fā)思想根植于敏捷方法中.它不但應(yīng)用于軟件開(kāi)發(fā)的生命周期,還應(yīng)用于技術(shù)技能的學(xué)習(xí)、需求采集、成品部署、用戶培訓(xùn)等方面.它包括了軟件開(kāi)發(fā)各個(gè)方面的所以活動(dòng).
為什么要進(jìn)行持續(xù)開(kāi)發(fā)呢?因?yàn)檐浖_(kāi)發(fā)是一項(xiàng)
非常復(fù)雜的智力活動(dòng),你遺留下來(lái)的任何問(wèn)題,要
么僥幸不會(huì)發(fā)生意外,要么情況會(huì)變得更糟糕,慢
慢惡化直到變得不可控制.當(dāng)問(wèn)題累積到一定程度的時(shí)候,事情就更難解決,最后無(wú)法扭轉(zhuǎn).面對(duì)這樣的問(wèn)題,唯一有效的解決辦法就是
持續(xù)地推進(jìn)系統(tǒng)前進(jìn)和完善(見(jiàn)《程序員修煉之道》一書(shū)中的"軟件熵"[HTOO]).
有些人對(duì)使用敏捷方法有顧忌,認(rèn)為它只是另一種危機(jī)管理而已.事實(shí)并非如此.危機(jī)管理是指問(wèn)題累計(jì)并且惡化,直到它們變得非常嚴(yán)重,以至于你不得不立即放下一切正在做的工作來(lái)解決危機(jī).而這樣又會(huì)帶來(lái)其他的負(fù)面影響,你就會(huì)陷入危機(jī)和恐慌的惡心循環(huán)中.這些正是你要避免的問(wèn)題.
所以,你要防微杜漸,把問(wèn)題解決在萌芽狀態(tài),你要探索未知領(lǐng)域,在大量成本投入之前先確定其可行性.你要知錯(cuò)能改,在事實(shí)面前主動(dòng)承認(rèn)自己的所有錯(cuò)誤.你要能自我反省,經(jīng)常編碼實(shí)戰(zhàn),加強(qiáng)團(tuán)隊(duì)協(xié)作精神.一開(kāi)始你可能會(huì)覺(jué)得不適應(yīng),因?yàn)檫@同以往有太多的不同,但是只要能真正地行動(dòng)起來(lái),習(xí)慣了,你就會(huì)得心應(yīng)手.
敏捷的修理之道
下面一句話是對(duì)敏捷的精辟概括.下面講扼要講述它的具體含義,以及敏捷的團(tuán)隊(duì)?wèi)?yīng)該采取什么樣的工作和生活方式。
首先,它要整個(gè)團(tuán)隊(duì)一起努力。敏捷團(tuán)隊(duì)往往是一個(gè)小型團(tuán)隊(duì),或者是大團(tuán)隊(duì)分成的若干小團(tuán)隊(duì)(10人左右).團(tuán)隊(duì)的所有成員在一起工作,,如果可能,最好有獨(dú)立的工作空間(或者類似bull pen),一起共享代碼和必要的開(kāi)發(fā)任務(wù),而且大部分時(shí)間都能在一起工作.同時(shí)和客戶或者軟件的用戶緊密工作在一起,并且盡可能早且頻繁地給他們演示最新的系統(tǒng).(注:bull pen 原指在棒球比賽中,候補(bǔ)投手的練習(xí)場(chǎng))
你要不斷從自己寫(xiě)的代碼中得到反饋,并且使用自動(dòng)化工具不斷地構(gòu)建(持續(xù)集成)和測(cè)試系統(tǒng).在前進(jìn)過(guò)程中,你都會(huì)有意識(shí)地修改一些代碼:在功能不變的情況下,重新設(shè)計(jì)部分代碼,改善代碼的質(zhì)量.這就是所謂的重構(gòu),它是軟件開(kāi)發(fā)中不可或缺的一部分---編碼永遠(yuǎn)沒(méi)有真正意義上的"結(jié)束".要以迭代的方式進(jìn)行工作:確定一小塊時(shí)間(一周左右)的計(jì)劃,然后按時(shí)完成它們.給客戶演示每個(gè)迭代的工作成果,及時(shí)得到他們的反饋(這樣可以保證方向正確),并且根據(jù)實(shí)際情況盡可能頻繁地發(fā)布系統(tǒng)版本讓用戶使用.對(duì)上述內(nèi)容有了了解之后,我們會(huì)從下面幾個(gè)方面更深入地走進(jìn)敏捷開(kāi)發(fā)的實(shí)踐.
第2章: 態(tài)度決定一切. 軟件開(kāi)發(fā)是一項(xiàng)智力勞動(dòng).在此章,我們會(huì)講解如何使用敏捷的心態(tài)開(kāi)始工作,以及一些有效的個(gè)人習(xí)慣.這會(huì)為你使用敏捷方法打下扎實(shí)的基礎(chǔ).
第3章:學(xué)無(wú)止境. 敏捷項(xiàng)目不可能坐享其成.除了開(kāi)發(fā)之外,我們還有在幕后進(jìn)行其他的訓(xùn)練,雖然它不屬于開(kāi)發(fā)工作本身,但卻對(duì)團(tuán)隊(duì)的發(fā)展極其重要.我們還將看到,如何通過(guò)培養(yǎng)習(xí)慣來(lái)幫助個(gè)人和團(tuán)隊(duì)成長(zhǎng)并自我超越.
第4章:交付用戶想要的軟件. 如果軟件不符合用戶的需求,無(wú)論代碼寫(xiě)得多么優(yōu)美,它都是毫無(wú)用處的.這里將會(huì)介紹一些客戶協(xié)作的習(xí)慣和技巧,讓客戶一直加入到團(tuán)隊(duì)的開(kāi)發(fā)中,學(xué)習(xí)他們的業(yè)務(wù)經(jīng)驗(yàn),并且保證項(xiàng)目符合他們的真正需求.
第5章:敏捷反饋. 敏捷團(tuán)隊(duì)之所以能夠順利開(kāi)展工作,而不會(huì)陷入泥潭掙扎導(dǎo)致項(xiàng)目失敗,就是因?yàn)橐恢笔褂梅答亖?lái)糾正軟件和開(kāi)發(fā)過(guò)程.最好的反饋源自代碼本身.本章將研究如何獲得反饋,以及如何更好地控制團(tuán)隊(duì)進(jìn)程和性能.
第6章:敏捷編程. 為滿足將來(lái)的需求而保持代碼的靈活和可變性,這是敏捷方法成功的關(guān)鍵.本章給出了一些習(xí)慣,介紹如何讓代碼更加整潔,具有更好的擴(kuò)展性,防止代碼慢慢變壞,最后變得不可收拾.
第7章:敏捷調(diào)試.調(diào)試錯(cuò)誤會(huì)占用很多項(xiàng)目開(kāi)發(fā)的時(shí)間---時(shí)間是經(jīng)不起浪費(fèi)的.這里將會(huì)學(xué)到一些提高調(diào)試效率的技巧,節(jié)省項(xiàng)目的開(kāi)發(fā)時(shí)間.
第8章:敏捷協(xié)作: 最后,一個(gè)敏捷開(kāi)發(fā)者已經(jīng)能夠獨(dú)當(dāng)一面,除此之外,你需要一個(gè)敏捷團(tuán)隊(duì).這里有一些最有效的實(shí)踐有助于黏合整個(gè)團(tuán)隊(duì),以及
其他一些實(shí)踐有助于團(tuán)隊(duì)日常事務(wù)和成長(zhǎng).
魔鬼和這些討厭的細(xì)節(jié)
如果你翻翻這本書(shū)就會(huì)注意到,在每節(jié)的開(kāi)頭我們都會(huì)引入一段話,旁邊配有一個(gè)魔鬼木刻像,誘使你養(yǎng)成不良習(xí)慣,如下所示.
"干吧,就走那個(gè)捷徑.真的,它可以為你節(jié)省時(shí)間.沒(méi)人會(huì)知道是你干的,這樣你就會(huì)加快自己的開(kāi)發(fā)進(jìn)度,并且能夠完成這些任務(wù)了.這就是關(guān)鍵所在."
他的有些話聽(tīng)上去有點(diǎn)兒荒唐,就像是Scott Adams筆下呆伯特(Dilbert)漫畫(huà)書(shū)中的魔王---"尖發(fā)老板"所說(shuō)的話一樣.但要記住Adams先生可是從他那些忠實(shí)的讀者中得到很多回饋的.
有些事情看上去就會(huì)讓人覺(jué)得很怪異,但這全部是我們親耳所聞、親眼所見(jiàn),或者是大家秘而不宣的事情,它們都是擺在我們面前的誘惑,不管怎樣,只要試過(guò)就會(huì)知道,為了節(jié)省項(xiàng)目的時(shí)間而走愚蠢的捷徑是會(huì)付出巨大代價(jià)的.
與這些誘惑相對(duì),在每個(gè)習(xí)慣最后,會(huì)出現(xiàn)以為守護(hù)天使,由她給出我們認(rèn)為你應(yīng)該遵循的一些良策.先難后易. 我們首先要解決困難的問(wèn)題,把簡(jiǎn)單的問(wèn)題留到最后.
現(xiàn)實(shí)中的事情很少是黑白分明的.我們將用一些段落描述一個(gè)習(xí)慣應(yīng)該帶給你什么樣的切身感受,并介紹成功實(shí)施和保持平衡的技巧.如下所示
切身感受
本段描述培養(yǎng)某個(gè)習(xí)慣應(yīng)該有什么樣的切身感受,如果在實(shí)踐中沒(méi)有這樣的體會(huì),你就要考慮改變一下實(shí)施的方法.
平衡的藝術(shù)
□ 一個(gè)習(xí)慣很可能會(huì)做得過(guò)火或者做得不夠.我們會(huì)給出一些建議,幫你掌握平衡,并告訴你一些技巧,能使習(xí)慣真正為你所用.畢竟,一件好事做得過(guò)火或者被誤用,都是非常危險(xiǎn)的(我們見(jiàn)過(guò)很多所謂的敏捷項(xiàng)目最后失敗,都是因?yàn)閳F(tuán)隊(duì)在實(shí)踐的時(shí)候沒(méi)有保持好自己的平衡).所以,我們希望保證你能真正從這些習(xí)慣中獲益.
通過(guò)遵循這些習(xí)慣,把握好平衡的藝術(shù),在真實(shí)世界中有效地應(yīng)用它們,你將會(huì)看到你的項(xiàng)目和團(tuán)隊(duì)發(fā)生了積極的變化.
好了,你將步入敏捷開(kāi)發(fā)者的修煉之路,更重要的是,你會(huì)理解其后的開(kāi)發(fā)原則.
第2章 態(tài)度決定一切
選定了要走的路,就是選定了它通往的目的地.
----Harry Emerson Fosdick(美國(guó)基督教現(xiàn)代主義神學(xué)家)
傳統(tǒng)的軟件開(kāi)發(fā)圖書(shū)一般先介紹一個(gè)項(xiàng)目的角色配置,然后是你需要產(chǎn)生哪些工件(artifact)----文檔、任務(wù)清單、甘特(Gantt)圖等,接著就是規(guī)則制,往往是這么寫(xiě)的:汝當(dāng)如此(注:或更通俗地寫(xiě)成:系統(tǒng)應(yīng)當(dāng)如何如何....)這般.....本書(shū)的風(fēng)格不是這樣的.歡迎進(jìn)入敏捷方法的世界,我們的做法有些不同.
例如,有一種相當(dāng)流行的軟件方法學(xué)要求對(duì)一個(gè)項(xiàng)目分配35種不同的角色,包括架構(gòu)師、設(shè)計(jì)人員、編碼人員、文檔管理者等.敏捷方法卻背道而馳.只需要一個(gè)角色:軟件開(kāi)發(fā)者,也就是你.項(xiàng)目需要什么你就做什么,你的任務(wù)就是和緊密客戶協(xié)作,一起開(kāi)發(fā)軟件.敏捷依賴人,而不是依賴于項(xiàng)目中的甘特圖和里程表.
圖表、集成開(kāi)發(fā)環(huán)境或者設(shè)計(jì)工具,它們本身都無(wú)法產(chǎn)生軟件,軟件是從你的大腦中產(chǎn)生的.而且它不是孤立的大腦活動(dòng),還會(huì)有許多其他方面的因素:個(gè)人情緒、辦公室的文化、自我主義、記憶力等.它們混為一體,態(tài)度和心情的瞬息變化都可能導(dǎo)致巨大的差別.
因此態(tài)度非常重要,包括你的和團(tuán)隊(duì)的.專業(yè)的態(tài)度應(yīng)該著眼于項(xiàng)目和團(tuán)隊(duì)的積極結(jié)果,關(guān)注個(gè)人和團(tuán)隊(duì)的成長(zhǎng),圍繞最后的成功開(kāi)展工作.由于很容易變成追求不太重要的目標(biāo),所以在本章,我們會(huì)專注于那些真正的目標(biāo).集中精力,你是為做事而工作.(想知道怎么做嗎?請(qǐng)見(jiàn)下一頁(yè).)
軟件項(xiàng)目時(shí)常伴有時(shí)間壓力---壓力會(huì)迫使你走捷徑,只看眼前利益.但是,任何一個(gè)有經(jīng)驗(yàn)的開(kāi)發(fā)者都會(huì)告訴你,欲速則不達(dá)(我們?cè)诘?5頁(yè)將介紹如何避免這個(gè)問(wèn)題).
我們每個(gè)人或多或少都有一些自我主要.一些人(暫且不提他們的名字)還美其名曰"健康"的自我主義.如果要我們?nèi)ソ鉀Q一個(gè)問(wèn)題,我們會(huì)為完成任務(wù)而感到驕傲,但這種驕傲有時(shí)會(huì)導(dǎo)致主觀和脫離實(shí)際.你也很可能見(jiàn)過(guò)設(shè)計(jì)方案的討論變成人身攻擊,而不是就事論事地討論問(wèn)題.對(duì)事不對(duì)人(第18頁(yè))會(huì)讓工作更加有效.
反饋是敏捷的基礎(chǔ).一旦你意識(shí)到走錯(cuò)了方向,就要立即做出決策,改變方向.但是指出問(wèn)題往往沒(méi)有那么容易,特別當(dāng)它涉及一些政治因素的時(shí)候.有時(shí)候,你需要勇氣去排除萬(wàn)難,奮勇前進(jìn)(第23頁(yè))
只有在你對(duì)項(xiàng)目、工作、事業(yè)有一個(gè)專業(yè)的態(tài)度時(shí),使用敏捷方法才會(huì)生效.如果態(tài)度不正確,那么所有的這些習(xí)慣都不管用.有了正確的態(tài)度,你才可以從這些方法中完全受益.下面我們就來(lái)介紹這些對(duì)你大有裨益的習(xí)慣和建議.
1.做事
"出了問(wèn)題,第一重要的是確定元兇.找到那個(gè)白癡!一旦證實(shí)了是他出的錯(cuò)誤,就可以保證這樣的問(wèn)題永遠(yuǎn)不會(huì)再發(fā)生了."
有時(shí)候,這個(gè)老魔頭的話聽(tīng)起來(lái)似乎很有道理.毫無(wú)疑問(wèn),你想把尋找罪魁禍?zhǔn)自O(shè)為最高優(yōu)先級(jí),難道不是嗎?肯定的答案是:不.最高優(yōu)先級(jí)應(yīng)該是解決問(wèn)題.
也許你不相信,但確實(shí)有些人常常不把解決問(wèn)題放在最高優(yōu)先級(jí)上.也許你也沒(méi)有.先自我反省一下,當(dāng)有問(wèn)題出現(xiàn)時(shí),"第一"反應(yīng)究竟是什么.
如果你說(shuō)的話只是讓事態(tài)更復(fù)雜,或者只是一味地抱怨,或者傷害了他人的感情,那么你無(wú)意中在給問(wèn)題火上澆油.相反,你應(yīng)該另辟蹊徑,問(wèn)問(wèn)"為了解決或緩解這個(gè)問(wèn)題,我能夠做些什么?"在敏捷的團(tuán)隊(duì)中,大家的重點(diǎn)是做事.你應(yīng)該把重點(diǎn)放到解決問(wèn)題上,而不是在指責(zé)犯錯(cuò)者上糾纏.
世上最糟糕的工作(除了在馬戲團(tuán)跟在大象后面打掃衛(wèi)生)就是和一群愛(ài)搬弄是非的人共事.他們對(duì)解決問(wèn)題并沒(méi)有興趣,相反,他們愛(ài)在別人背后
議論是非.他們挖空心思指手畫(huà)腳,議論誰(shuí)應(yīng)該受到指責(zé).這樣一個(gè)團(tuán)隊(duì)的生產(chǎn)力是極其低下的.如果你發(fā)現(xiàn)自己在這樣的團(tuán)隊(duì)中工作,不要從團(tuán)隊(duì)中走
開(kāi)----應(yīng)該跑開(kāi).至少要把對(duì)話從負(fù)面的指責(zé)游戲引到中性的話題,比如談?wù)擉w育運(yùn)動(dòng)(紐約揚(yáng)基隊(duì)最近怎么樣)或者天氣.
在敏捷團(tuán)隊(duì)中,情形截然不同.如果你向敏捷團(tuán)隊(duì)中的同事抱怨,他們會(huì)說(shuō):"好,我能幫你做些什么呢?"他們把精力直接放到解決問(wèn)題上,而不是抱怨.他們的動(dòng)機(jī)很明確,重點(diǎn)就是做事.不是為了自己的面子,也不是為了指責(zé),也無(wú)意進(jìn)行個(gè)人智力角斗.
你可以從自己先做起.如果一個(gè)開(kāi)發(fā)者帶著抱怨或問(wèn)題來(lái)找你,你要了解具體的問(wèn)題,詢問(wèn)他你能提供什么樣的幫助.這樣簡(jiǎn)單的一個(gè)行為就清晰地表明你的目的是解決問(wèn)題,而不是追究責(zé)任,這樣就會(huì)消除他的顧慮.你是給他們幫忙的.這樣,他們會(huì)知道每次走進(jìn)你的時(shí)候,你會(huì)真心幫助他們解決問(wèn)題.他們可以來(lái)找你把問(wèn)題解決了,當(dāng)然還可以繼續(xù)去別處求助.
如果你找人幫忙,卻沒(méi)人積極響應(yīng),那么你應(yīng)該主動(dòng)引導(dǎo)對(duì)話.解釋清楚你想要什么,并清晰地表明你的目的是解決問(wèn)題,而不是指責(zé)他人或者進(jìn)行爭(zhēng)辯.
指責(zé)不會(huì)修復(fù)bug. 把矛頭對(duì)準(zhǔn)問(wèn)題的是解決辦法,而不是人.這是真正有用處的正面效應(yīng).
切身感受
勇于承認(rèn)自己不知道答案,這會(huì)讓人感覺(jué)放心.一個(gè)重大的錯(cuò)誤應(yīng)該被當(dāng)作一次學(xué)習(xí)而不是指責(zé)他人的機(jī)會(huì).團(tuán)隊(duì)成員們?cè)谝黄鸸ぷ?應(yīng)該互相幫助,而不是互相指責(zé).
平衡的藝術(shù)
□ "這不是我的錯(cuò)",這句話不對(duì)."這就是你的錯(cuò)",這句話更不對(duì).
□ 如果你沒(méi)有犯過(guò)任何錯(cuò)誤,就說(shuō)明你可能沒(méi)有努力去工作.
□ 開(kāi)發(fā)者和質(zhì)量工程師(QA)爭(zhēng)論某個(gè)問(wèn)題是系統(tǒng)本身的缺陷還是系統(tǒng)增強(qiáng)功能導(dǎo)致的,通常沒(méi)有多大的意義.與其如此,不如趕緊去修復(fù)它.
□ 如果一個(gè)團(tuán)隊(duì)成員誤解了一個(gè)需求、一個(gè)API調(diào)用,或者最近一次會(huì)議做的決策,那么,也許就以為這團(tuán)隊(duì)的其他成員也有相同的誤解.要確保整個(gè)團(tuán)隊(duì)盡快消除誤解.
□ 如果一個(gè)團(tuán)隊(duì)成員的行為一再傷害了團(tuán)隊(duì),則他表現(xiàn)得很不職業(yè).那么他就不是在幫助團(tuán)隊(duì)向解決問(wèn)題的方向前進(jìn).這種情況下,我們必須要求他離開(kāi)這個(gè)團(tuán)隊(duì).(不需要解雇他,但是他不能繼續(xù)留在這個(gè)項(xiàng)目中.同時(shí)也要意識(shí)到,頻繁的人員變動(dòng)對(duì)整個(gè)團(tuán)隊(duì)的平衡也很危險(xiǎn))
2. 欲速則不達(dá)
"你不需要真正地理解那塊代碼,它只要能夠工作就可以了.哦,它需要一個(gè)小小的調(diào)整.只要在結(jié)果中再加上幾行代碼,它就可以工作了.干吧!就把那幾行代碼加進(jìn)去,它應(yīng)該可以工作."
我們經(jīng)常會(huì)遇到這種情況,出現(xiàn)了一個(gè)bug,并且時(shí)間迫切.快速修復(fù)確實(shí)可以解決它---只要新加一行代碼或者忽略那個(gè)列表上的最后一個(gè)條目,它就可以工作了.但接下來(lái)的做法才能說(shuō)明,誰(shuí)是優(yōu)秀的程序員,誰(shuí)是拙劣的代碼工人.
拙劣的代碼工人會(huì)這樣不假思索地改完代碼,然后快速轉(zhuǎn)向下一個(gè)問(wèn)題.
優(yōu)秀的程序員會(huì)挖掘更深一層,盡力去理解為什么這里必須要加1,更重要的是,他會(huì)想明白會(huì)產(chǎn)生什么其他影響.
也許這個(gè)例子聽(tīng)起來(lái)有點(diǎn)做作,甚至你會(huì)覺(jué)得很無(wú)聊.但是,真實(shí)世界中有大量這樣的事情發(fā)生.Andy以前的一個(gè)客戶正遇到過(guò)這樣的問(wèn)題.沒(méi)有一個(gè)開(kāi)發(fā)者或者架構(gòu)師知道他們業(yè)務(wù)領(lǐng)域的底層數(shù)據(jù)模型.而且,通過(guò)幾年的累計(jì),代碼里有成千上萬(wàn)的+1和-1修正.在這樣臟亂的代碼中添加新的功能或者修復(fù)bug,就難逃脫發(fā)的噩運(yùn)(事實(shí)上,很多開(kāi)發(fā)者因此而禿頂).
千里之堤,毀于蟻穴,大災(zāi)難是逐步演化來(lái)的.一次又一次的快速修復(fù),每一次都不探究問(wèn)題的根源,久而久之就形成了一個(gè)危險(xiǎn)的沼澤地,最終會(huì)吞噬整個(gè)項(xiàng)目中的生命.
在工作壓力下,不去深入了解真正的問(wèn)題以及可能的后果,
就快速修改代碼,這樣只是解決表面問(wèn)題,最終會(huì)引發(fā)大問(wèn)題.
快速修復(fù)的誘惑,很容易令人把持不住,墜入其中.短期看,它
似乎使有效的.但從長(zhǎng)遠(yuǎn)來(lái)看,無(wú)異于穿越一片沙流,你也許僥幸走過(guò)了一半的路程(甚至更遠(yuǎn)),一切似乎都很
正常.但是轉(zhuǎn)眼間悲劇就發(fā)生了....
只要我們繼續(xù)進(jìn)行快速修復(fù),代碼的清晰度就不斷降低.一旦問(wèn)題累計(jì)到一定程度,清晰的代碼就不復(fù)存在,只剩一片渾濁.很可能在你的公司就有人這樣告訴你:"無(wú)論如何,千萬(wàn)不能碰那個(gè)模塊的代碼.寫(xiě)代碼那哥們已經(jīng)不在這兒了,沒(méi)有人看得懂他們的代碼."這些代碼根本沒(méi)有清晰度可言.它已經(jīng)成為一團(tuán)迷霧,無(wú)人能懂.
如果你在團(tuán)隊(duì)中這樣的事情發(fā)生,那么你是不可能敏捷的.但是敏捷方法中的一些技術(shù)可以阻止這樣的事情發(fā)生.這里只是一些概述,后面的章節(jié)會(huì)有更深入的介紹.
孤立非常危險(xiǎn),不要讓開(kāi)發(fā)人員完全孤立地編寫(xiě)代碼(見(jiàn)第155頁(yè),習(xí)慣40).如果團(tuán)隊(duì)成員花些時(shí)間閱讀其他同事寫(xiě)的代碼,他們就能確保代碼是可
讀和可理解的,并且不會(huì)隨意加入這些"+1或-1"的代碼.閱讀代碼的頻率越高越好.實(shí)行代碼復(fù)審,不僅有助于代碼更好理解,而且是發(fā)生bug最有效的方法之一(見(jiàn)第165頁(yè),習(xí)慣44).
另一種防止代碼難懂的重要技術(shù)就是單元測(cè)試.單元測(cè)試幫助你很自然地把代碼分層,分成很多可管理的小塊,這樣就會(huì)得到設(shè)計(jì)更好,更清晰的代碼.更深入項(xiàng)目的時(shí)候,你可以直接閱讀單元測(cè)試---它們是一種可執(zhí)行的文檔(見(jiàn)第78頁(yè),習(xí)慣
19).有了單元測(cè)試,你會(huì)看到更小、更易于理解的代碼模塊,運(yùn)行和使用代碼,能夠幫助你徹底理解這些代碼.: 不要墜入快速的簡(jiǎn)單修復(fù)之中.要投入時(shí)間和精力保持代碼的整潔、敞亮。
切身感受
在項(xiàng)目中,代碼應(yīng)該是很亮堂的,不應(yīng)該有黑暗死角。你也許不知道每塊代碼的每個(gè)細(xì)節(jié),或者每個(gè)算法的每個(gè)步驟,但是你對(duì)整體的相關(guān)知識(shí)有很好的了解。沒(méi)有任何一塊代碼被警戒線或者"切勿入內(nèi)"的標(biāo)志隔離開(kāi).
平衡的藝術(shù)
□ 你必須要理解一塊代碼是如何工作的,但是不一定需要成為一位專家.只要你能使用它進(jìn)行有效的工作就足夠了,不需要把它當(dāng)作畢業(yè)生事業(yè).
□ 如果有一位團(tuán)隊(duì)成員宣布,有一塊代碼其他人都很難看懂,這就意味著任何人(包括原作者)都很難維護(hù)它.請(qǐng)讓它變得簡(jiǎn)單些.
□ 不要急于修復(fù)一段沒(méi)能真正理解的代碼.這種+1/-1的病癥始于無(wú)形,但是很快就會(huì)讓代碼一團(tuán)糟.要解決真正的問(wèn)題,不要治標(biāo)不治本.
□ 所有的大型系統(tǒng)都非常復(fù)雜,因此沒(méi)有一個(gè)人能完全明白所有的代碼.除了深入了解你正在開(kāi)發(fā)的那部分代碼之外,你還需要從更高的層面來(lái)了解大部分代碼的功能,這樣就可以理解系統(tǒng)各個(gè)功能塊之間都是如何交互的.
· 如果系統(tǒng)的代碼已經(jīng)惡化,可以閱讀第23頁(yè)習(xí)慣4中給出的建議.
對(duì)事不對(duì)人:
你在這個(gè)設(shè)計(jì)上投入了很多精力,為他付出很多心血。你堅(jiān)信它比其他任何人的設(shè)計(jì)都棒。別聽(tīng)他們的,他們只會(huì)把問(wèn)題變得更糟糕。”
你很可能見(jiàn)過(guò),對(duì)方案設(shè)計(jì)的討論失控變成了情緒化的指責(zé) 做決定是基于誰(shuí)提出了這個(gè)觀點(diǎn),而不是權(quán)衡觀點(diǎn)本身的利弊。我們?cè)?jīng)參與過(guò)那樣的會(huì)議,最后鬧的大家都很不愉快。
但是,這也很正常。當(dāng)Lee先生在做一個(gè)新的方案介紹的時(shí)候,下面有人會(huì)說(shuō):“那樣很蠢!”(這也就暗示著Lee先生也很蠢。)如果把這句話推敲一下,也許會(huì)好一點(diǎn):“那樣很蠢,你忘記考慮它要線程安全。”事實(shí)上最適合并且最有效的表達(dá)方式應(yīng)該是:“謝謝,Lee先生。但是我想知道,如果兩個(gè)用戶同時(shí)登錄會(huì)發(fā)生什么情況?”
看出其中的不同了吧!下面我們來(lái)看看對(duì)一個(gè)明顯的錯(cuò)誤有哪些常見(jiàn)的反應(yīng)。
□ 否定個(gè)人能力。
□ 指出明顯的缺點(diǎn),并否定其觀點(diǎn)。
□ 詢問(wèn)你的隊(duì)友,并提出你的顧慮。
第一種方法是不可能成功的。即使Lee先生是一個(gè)十足的笨蛋,很小的問(wèn)題也搞不定,但你那樣指出問(wèn)題根本不會(huì)對(duì)他的水平有任何的提高,反而會(huì)導(dǎo)致他以后再也不會(huì)提出自己的任何想法了。第二種方法至少觀點(diǎn)明確,但也不能給Lee太多的幫助,甚至可能會(huì)讓你自己惹火上身。也許Lee能巧妙地回復(fù)你對(duì)非線程安全的職責(zé):“哦,不過(guò)它不需要多線程。因?yàn)樗辉贔rozbot模塊的環(huán)境中使用,它已經(jīng)運(yùn)行在自己的線程中了。”哎喲!忘記了Frozbot這一茬了。現(xiàn)在該是你覺(jué)得自己蠢了,Lee也會(huì)因?yàn)槟懔R他笨蛋而生氣。
現(xiàn)在看看第三種方法。沒(méi)有譴責(zé),沒(méi)有評(píng)判,只是簡(jiǎn)單的表達(dá)自己的觀點(diǎn)。讓Lee自己意識(shí)到這個(gè)問(wèn)題,而不是掃他的面子。由此可以開(kāi)始一次交談,而不是爭(zhēng)辯。
在一個(gè)需要緊密合作的開(kāi)發(fā)團(tuán)隊(duì)中,如果能稍加注意禮貌對(duì)待他人,將會(huì)有益于整個(gè)團(tuán)隊(duì)關(guān)注有真正價(jià)值的問(wèn)題,而不是勾心斗角,誤入歧途。我們每個(gè)人都能有一些極好的創(chuàng)新想法,同樣也會(huì)萌生一些很蠢的想法。
如果你準(zhǔn)備提出一個(gè)想法,卻擔(dān)心有可能被嘲笑,或者你要提出一個(gè)建議,卻當(dāng)心自己丟面子,那么你就不會(huì)主動(dòng)提出自己的建議了。 ,好的軟件開(kāi)發(fā)作品和好的軟件設(shè)計(jì),都需要大量的創(chuàng)造力和洞察力。分享并融合各種不同的想法和觀點(diǎn),遠(yuǎn)遠(yuǎn)勝于單個(gè)想法為項(xiàng)目帶來(lái)的價(jià)值、
負(fù)面的評(píng)論和態(tài)度扼殺了創(chuàng)新。現(xiàn)在我們并不是提倡在設(shè)計(jì)方案的會(huì)議上手拉手唱《學(xué)習(xí)雷鋒好榜樣》,這
樣也會(huì)降低會(huì)議的效率。但是,你必須把重
點(diǎn)放在解決問(wèn)題上,而不是去極力證明誰(shuí)的
注意更好。在團(tuán)隊(duì)中,一個(gè)人只是智商高是
沒(méi)用的,如果他還很頑固并且拒絕合作,那就
更糟糕。在這樣的團(tuán)隊(duì)中,生產(chǎn)率和創(chuàng)新率都會(huì)冰凌滅亡的邊緣。
我們每個(gè)人都會(huì)有好的想法,也會(huì)有不對(duì)的想法,團(tuán)隊(duì)中的每個(gè)人都需要自由地表達(dá)觀點(diǎn)。即使你的建議不被全盤接受,也能對(duì)最終解決問(wèn)題有所幫助。不要害怕受到批評(píng)。記住,任何一個(gè)專家都是從這里開(kāi)始的。用Les Brown的一句話說(shuō)就是:“你不需要很出色才能起步,但是你必須起步才能變的很出色。”
下面是一些有效的特殊技術(shù):
設(shè)定最終期限。如果你正在參加設(shè)計(jì)方案討論會(huì),或者是尋找解決方案是遇到問(wèn)題,清設(shè)定一個(gè)明確的最終期限,例如午飯時(shí)間或者一天的結(jié)束。這樣的時(shí)間限制可以防止人們陷入無(wú)休止的理論爭(zhēng)辯中,保證團(tuán)隊(duì)工作的順利進(jìn)行。同時(shí)(我們覺(jué)得)應(yīng)現(xiàn)實(shí)一些:沒(méi)有最好的答案,只有更適合的方案。設(shè)定期限能夠幫你在為難的時(shí)候果斷做出決策,讓工作可以繼續(xù)進(jìn)行。
逆向思維。團(tuán)隊(duì)中的每個(gè)成員都應(yīng)該意識(shí)到權(quán)衡的必要性。一種客觀對(duì)待問(wèn)題的辦法是:先是積極地看到他的正面,然后再努力地從反面去認(rèn)識(shí)他。目的是要找出優(yōu)點(diǎn)最多缺點(diǎn)最少的那個(gè)方案,而這個(gè)好辦法可以盡可能地發(fā)現(xiàn)其優(yōu)缺點(diǎn)。這也有助于少帶個(gè)人感情。
設(shè)立仲裁人。在會(huì)議的開(kāi)始,選擇一個(gè)仲裁人作為本次會(huì)議的決策者。每個(gè)人都要有機(jī)會(huì)針對(duì)問(wèn)題暢所欲言。仲裁人的責(zé)任就是確保每個(gè)人都有發(fā)言的機(jī)會(huì),并維持會(huì)議的正常進(jìn)行。仲裁人可以防止明星員工操作會(huì)議,并及時(shí)打斷假大空式發(fā)言。
如果你自己沒(méi)有積極參與這次討論活動(dòng),那么你最好退一步做會(huì)議的監(jiān)督者。仲裁人應(yīng)該專注于調(diào)停,而不是發(fā)表自己的觀點(diǎn)(理想情況不應(yīng)該在整個(gè)項(xiàng)目中有既得利益)。當(dāng)然,這項(xiàng)任務(wù)不需要嚴(yán)格的技術(shù)技能,需要的是和他人打交道的你能力。
支持已經(jīng)做出決定。 一旦方案被確定了(不管是什么樣的方案), 每個(gè)團(tuán)隊(duì)成員都必須通力合作, 努力實(shí)現(xiàn)這個(gè)方案。 每個(gè)人都要時(shí)刻記住, 我們的目標(biāo)是讓項(xiàng)目成功滿足用戶需求。 客戶并不關(guān)心是誰(shuí)的注意 他們關(guān)心的是,這個(gè)軟件是否可以工作,并且是否符合他們的期望。結(jié)果最重要、
實(shí)際充滿了妥協(xié)(生活本省也是如此),成功屬于意識(shí)到這一點(diǎn)的團(tuán)隊(duì)。工作中不感情用事是需要克制力的,而你若能彰顯出成熟大度來(lái),大家一定不會(huì)視而不見(jiàn)。這需要有人帶頭,身體力行,去感染另一部分人。
: 對(duì)事不對(duì)人。 讓我們驕傲的應(yīng)該是解決了問(wèn)題,而不是比較出誰(shuí)的主意更好。
切身感受
一個(gè)團(tuán)隊(duì)能夠很公正地討論一些方案的優(yōu)點(diǎn)和缺點(diǎn),你不會(huì)因?yàn)榫芙^了有太多缺點(diǎn)的方案而傷害別人,也不會(huì)因?yàn)椴杉{了某個(gè)不甚完美(但是更好的)解決方案而被人忌恨。
平衡的藝術(shù)
□ 經(jīng)歷貢獻(xiàn)自己的好想法,如果你的想法沒(méi)有被采納也無(wú)需生氣。不要因?yàn)橹皇窍塍w現(xiàn)自己的想法而對(duì)擬定的好思路畫(huà)蛇添足。
□ 脫離實(shí)際的反觀點(diǎn)會(huì)使?fàn)幷撟兾丁H魧?duì)一個(gè)想法有成見(jiàn),你很容易提出一堆不太可能發(fā)生或者不太實(shí)際的情形去批駁它。這時(shí),清先捫心自問(wèn):類似問(wèn)題以前發(fā)生過(guò)嗎?是否經(jīng)常發(fā)生?
□ 也就是說(shuō),想這樣說(shuō)是不夠的:我們不能采用這個(gè)方案,因?yàn)閿?shù)據(jù)庫(kù)廠商可能會(huì)倒閉。或者:用戶絕對(duì)不會(huì)接受那個(gè)方案。你必須要評(píng)判那些場(chǎng)景發(fā)生的可能性有多大。想要支持或者反駁一個(gè)觀點(diǎn),有時(shí)候你必須先做一個(gè)原型或者調(diào)查出它有多少的同意者或者反對(duì)者。
□ 只有更好,沒(méi)有最好。盡管“最佳實(shí)踐”這個(gè)術(shù)語(yǔ)到處在用,但實(shí)際上不存在“最佳”,只有在某個(gè)特定條件下更好的實(shí)踐、
□ 不帶個(gè)人情緒并不是要盲目地接受所有的觀點(diǎn)。用合適的詞和理由去解釋為什么你不贊同這個(gè)觀點(diǎn)或方案,并提出明確的問(wèn)題。
4.排除萬(wàn)難,奮勇前進(jìn)
“如果你發(fā)現(xiàn)其他人的代碼有問(wèn)題,只要你自己心里知道就行了。畢竟,你不想傷害他們,或者惹來(lái)麻煩。如果他是你的老板,更好格外謹(jǐn)慎,只要按照他的命令執(zhí)行就可以了。”
有一則寓言叫“誰(shuí)去給貓系鈴鐺”(who Will Bell the Cat)。老鼠們打算在貓的脖子上系一個(gè)鈴鐺,這樣貓巡邏靠近的時(shí)候,就能預(yù)先得到警報(bào)。每只老鼠都點(diǎn)頭,熱為這是一個(gè)絕妙的想法。這是一只年老的老鼠問(wèn)道:“那么,誰(shuí)愿意挺身而出去系鈴鐺呢?”毫無(wú)疑問(wèn),沒(méi)有一只老鼠站出來(lái)。當(dāng)然,計(jì)劃也就這樣泡湯了。
有時(shí),絕妙的計(jì)劃會(huì)因?yàn)橛職獠蛔愣罱K失敗。盡管前方很危險(xiǎn) 不管是真魚(yú)雷或者只是一個(gè)比喻 你必須有勇氣向錢沖鋒,做你認(rèn)為對(duì)的事情、
加入要你修復(fù)其他人編寫(xiě)的代碼,而代碼很難理解也不好使用。你是應(yīng)該繼續(xù)修復(fù)工作,保留這些臟亂的代碼呢,還是應(yīng)該告訴你的老板,這些代碼太爛了,應(yīng)該通通扔掉呢?
也許你會(huì)跳起來(lái)告訴周圍的人,那些代碼是多么的糟糕,但那只是抱怨和發(fā)泄,并不能解決問(wèn)題。相反,你應(yīng)該重寫(xiě)這些代碼,并比較重寫(xiě)前后的優(yōu)缺點(diǎn)。動(dòng)手證明(不要只是嚷嚷)最有效的方式,是把糟糕的代碼放到一邊,立刻重寫(xiě)。列出重寫(xiě)的理由,會(huì)有助于你的老板(以及同事)認(rèn)清當(dāng)前形勢(shì),幫助他們得到正確的解決方案。
在假定你在處理一個(gè)特定的組件。突然,你發(fā)現(xiàn)完全弄錯(cuò)了,你需要推翻重來(lái)。當(dāng)然,你也會(huì)很擔(dān)心向團(tuán)隊(duì)其他成員說(shuō)明這個(gè)問(wèn)題,以爭(zhēng)取跟多的時(shí)間和幫助。
當(dāng)發(fā)現(xiàn)問(wèn)題時(shí), 不要試圖掩蓋這些問(wèn)題。而要有勇氣站起來(lái), 說(shuō):“我現(xiàn)在知道了,我過(guò)去使用的方法不對(duì)。 我想到了一些方法, 可以解決這個(gè)問(wèn)題 如果你有更好的想法,我也很樂(lè)意聽(tīng)一聽(tīng) 但可能會(huì)花多些時(shí)間。”你已經(jīng)把所有的問(wèn)題的負(fù)面情緒拋諸腦后,你的意圖很清楚,就是尋找解決方案。既然你提出大家一起努力解決問(wèn)題,那就你不回有任何爭(zhēng)辯的余地。這樣會(huì)促進(jìn)大家去解決問(wèn)題。也許,他們就會(huì)主動(dòng)走進(jìn),提供幫助。更重要的是,這顯示出了你的真誠(chéng)和勇氣,同時(shí)你也贏得了他們的信任。
你深知怎樣做才是正確的,或者至少知道目前的做法是錯(cuò)誤的。要有勇氣想其他的項(xiàng)目成員、老板、或者客戶解釋你的不同觀點(diǎn)。當(dāng)然,這并不容易。也許你會(huì)拖延項(xiàng)目的進(jìn)度,冒犯項(xiàng)目經(jīng)理,甚至惹惱投資人。但你都要不顧一切,向著正確的方向奮力前進(jìn)。
美國(guó)南北戰(zhàn)爭(zhēng)時(shí)的海軍將Dabid Farragut曾經(jīng)說(shuō)過(guò)一句名言:“別管他媽的魚(yú)雷,Drayton上校,全速前進(jìn)。”確實(shí),前面埋伏著水雷(那時(shí)叫魚(yú)雷),但是要突破防線,只有全速前進(jìn)(事實(shí)上,Farragut 的原話往往被簡(jiǎn)化為:“別管他媽的魚(yú)雷,全速前進(jìn)!”)。
切身感受
勇氣會(huì)讓人覺(jué)得有點(diǎn)不自在,提前鼓足勇氣更需要魄力。但有些時(shí)候,它是掃除障礙的唯一途徑,否則問(wèn)題就會(huì)進(jìn)一步惡化下去。鼓起你的勇氣,這能讓你從恐懼中解脫出來(lái)。
平衡的藝術(shù)
· 如果你說(shuō)天快要塌下來(lái)了,但其他團(tuán)隊(duì)成員都并不贊同。反思一下,也許你是正確的,但你沒(méi)有解釋清楚自己的理由。
· 如果你說(shuō)天快要塌下來(lái)了但其他團(tuán)隊(duì)成員都不贊同。認(rèn)真考慮一下,他們也許是對(duì)的。
· 如果設(shè)計(jì)或代碼中出現(xiàn)了奇怪的問(wèn)題,花時(shí)間去理解為代碼會(huì)是這樣的。如果你找到了解決的辦法,但代碼仍然另人費(fèi)解,唯一的解決辦法就是重構(gòu)代碼,讓他可讀性更強(qiáng)。如果你沒(méi)有馬上理解那段代碼,不要輕易的否定和重寫(xiě)他們。那不是勇氣,而是魯莽。
· 當(dāng)你勇敢站出來(lái)提問(wèn)時(shí),如果收到了缺乏背景知識(shí)的抉擇者的抵制,你需要用他們能夠聽(tīng)懂的話語(yǔ)表達(dá)。“更清晰的代碼”是無(wú)法打動(dòng)生意人的。節(jié)約資金、獲取更好的投資回報(bào),避免訴訟以及增加用戶利益,會(huì)讓論點(diǎn)更具說(shuō)服力。
· 如果你在壓力下要對(duì)代碼質(zhì)量做出妥協(xié),你可以指出,作為一名開(kāi)發(fā)者,你沒(méi)有職權(quán)毀壞公司的資產(chǎn)(所有的代碼)。
□ 學(xué)無(wú)止境
即使你已經(jīng)在正確的軌道上,但如果只是停止不前,也仍然會(huì)被淘汰出局。Will Rogers (美國(guó)著名演員)
敏捷需要持續(xù)不斷的學(xué)習(xí)和充電。正如上面引用的Will Rogers的話,逆水行舟,不進(jìn)則退。那不僅是賽馬場(chǎng)上的真理,它更適合我們當(dāng)今的程序員。
軟件開(kāi)發(fā)行業(yè)是一個(gè)不停發(fā)展和永遠(yuǎn)變化的領(lǐng)域。雖然有一些概念一直有用,但還有很多只是很快就會(huì)過(guò)時(shí)。從事軟件開(kāi)發(fā)行業(yè)就像是在跑步機(jī)上,你必須一直跟上步伐穩(wěn)步前進(jìn),狗則就會(huì)摔倒出局。
誰(shuí)會(huì)幫助你保持步伐前進(jìn)呢?在一個(gè)企業(yè)化的社會(huì)中,只有一個(gè)人會(huì)為你負(fù)責(zé)------那就是你自己。是否能跟上變化,完全取決于你自己。
許多新技術(shù)都基于現(xiàn)有的技術(shù)和思想。他們會(huì)加入一些新的東西,這些新東西是逐步加入的量,如果你跟蹤技術(shù)變化,那么學(xué)習(xí)這些新東西對(duì)你來(lái)說(shuō)就是了解這些增量變化。如果你不跟蹤變化,技術(shù)變化就會(huì)顯得很突然并且難以應(yīng)付。這就好比少小離家老大回,你會(huì)發(fā)現(xiàn)變化很大,甚至有很多地方都不認(rèn)識(shí)。然而,居住在那里的人們,每天只看到小小的變化,所以非常適應(yīng)。在第28頁(yè)我們會(huì)介紹一些跟蹤變化的方法。
給自己投資,讓自己與時(shí)俱進(jìn),當(dāng)然在好不過(guò),但是也要努力對(duì)團(tuán)隊(duì)投資,這個(gè)目標(biāo)怎么實(shí)現(xiàn)呢?你將從第31頁(yè)學(xué)到實(shí)現(xiàn)這個(gè)目標(biāo)的一些方法。
學(xué)習(xí)新技術(shù)和新的開(kāi)發(fā)方法很重要,同時(shí)你也要能摒棄陳舊和過(guò)時(shí)的開(kāi)發(fā)方法。換句話說(shuō),你需要懂得丟棄(請(qǐng)閱讀第34頁(yè))
當(dāng)我們談到變化這個(gè)話題的時(shí)候,要認(rèn)識(shí)到你對(duì)問(wèn)題的理解在整個(gè)項(xiàng)目期間也是在變化的。你曾經(jīng)認(rèn)為自己已經(jīng)很明白的事情,現(xiàn)在也許并不是你想象中的那樣。你要對(duì)沒(méi)有去年全理解的某些疑問(wèn)不懈的深入追蹤下去,我們將從第37頁(yè)開(kāi)始講述為什么要打破沙鍋問(wèn)到底,以及如何有效地提問(wèn)。
最后,一個(gè)活力十足的冥界開(kāi)發(fā)團(tuán)隊(duì)需要有規(guī)律反復(fù)地做很多事情,一旦項(xiàng)目開(kāi)始運(yùn)作你就要把握開(kāi)發(fā)節(jié)奏,我們會(huì)在第40頁(yè)介紹這種節(jié)奏感
5.跟蹤變化軟件技術(shù)的變化如此之快,勢(shì)不可擋,這是它的本性。繼續(xù)用你熟悉的語(yǔ)言做你的老本行吧,你不可能跟上技術(shù)變化的腳步。
赫拉克利說(shuō)過(guò):“唯有變化是永恒的。”歷史已經(jīng)證明了這句真理,在當(dāng)今快速發(fā)展的IT時(shí)代尤其如此。
你從事的是一項(xiàng)充滿激情且不停變化的工作。如果你畢業(yè)于計(jì)算機(jī)相關(guān)的專業(yè),并覺(jué)得自己已經(jīng)學(xué)完了所有知識(shí),那你就大錯(cuò)特錯(cuò)了。假設(shè)你是10多年前的1995年畢業(yè)的, 那時(shí),你掌握了哪些技術(shù)呢? 可能你的C++還學(xué)得不錯(cuò),
你了解有一門新的語(yǔ)言交java,一種被稱作是設(shè)計(jì)模式的思想開(kāi)始引起大家的關(guān)注。一些人會(huì)談?wù)摫环Q作因特網(wǎng)的東東。如果那個(gè)時(shí)候你就不再學(xué)習(xí),而在2005年的時(shí)候重出江湖。再看看周圍,就會(huì)發(fā)現(xiàn)變化巨大,就算是在一個(gè)相當(dāng)狹小的技術(shù)領(lǐng)域,要學(xué)習(xí)那些新技術(shù)并達(dá)到熟練的程度,一年的時(shí)候也不夠。
技術(shù)發(fā)展的步伐如此快速,簡(jiǎn)直讓人難以置信。就以java為例,你掌握了java語(yǔ)言及其一系列的最新特性。接著你要掌握Swing,JSP,Struts,Tapestry,jsf,JDBC,JDO,Hibernate,JMS,EJB,Lucene,Spring
….還可以列舉很多,如果你使用的是微軟的技術(shù),要掌握VB, VisualC++, MFC, COM, ATL, .NET,C#, VB.NET , ASP..NET,ADO.NET, WinForm, Enterprise, Service,Biztalk,….并且不要忘記還有UML,
Ruby,XML,DOM,SAX,JAXP,JDOM,XSL,Schema,SOAP,Web Service, SOA,同樣還可以繼續(xù)列舉下去(我們將會(huì)用光所有的縮寫(xiě)字母)。不幸的是,如果只是掌握了工作中需要的技術(shù)并不夠。那樣的工作也許幾年之后就不再有了----它會(huì)被外包或者會(huì)過(guò)時(shí),那么你也就會(huì)出局。假設(shè)你是Visual C++或者VB程序員,看到Com技術(shù)出現(xiàn)了,你花時(shí)間去學(xué)習(xí)它(雖然很痛苦),并且隨時(shí)了解分布式對(duì)象計(jì)算的一切。當(dāng)xml出現(xiàn)的時(shí)候,你花時(shí)間去學(xué)習(xí)它。你深入研究ASP,熟知如何用它來(lái)開(kāi)發(fā)WEB應(yīng)用。你雖然不是這些技術(shù)的專家,但也不是對(duì)它們一無(wú)所知。好奇心促使你去了解MVC是什么。
如果你跟上了這些新技術(shù),接下來(lái)學(xué)習(xí).NET就不再是大問(wèn)題。你不需要一口氣爬上10樓,而需要一直在攀登,所以最后看起來(lái)就像只要再上一二層,如果你對(duì)所有這些技術(shù)都一無(wú)所知,想要馬上登上十樓,
肯定會(huì)讓你喘不過(guò)氣來(lái)。而且,這也會(huì)花很長(zhǎng)時(shí)間,期間還會(huì)有更新的技術(shù)出現(xiàn)。如何才能跟上技術(shù)變化的步伐呢?幸好,現(xiàn)今有很多方法和工具可以幫助我們繼續(xù)充電。下面是一些建議。
迭代和增量式學(xué)習(xí)。每天計(jì)劃用一段時(shí)間來(lái)學(xué)習(xí)新技術(shù),它不需要很長(zhǎng)時(shí)間,但需要經(jīng)常進(jìn)行。記下哪些你想學(xué)習(xí)的東西-------當(dāng)你聽(tīng)到一些不熟悉的術(shù)語(yǔ)或者短語(yǔ)時(shí),簡(jiǎn)要地把它記錄下來(lái)。然后再計(jì)劃的時(shí)間中深入研究它。
了解最新行情。互聯(lián)網(wǎng)上有大量關(guān)于學(xué)習(xí)新技術(shù)的資源。閱讀社區(qū)討論的和郵件列表,可以了解其他人遇到的問(wèn)題,以及他們發(fā)現(xiàn)的很酷的解決方案。額一些公認(rèn)的優(yōu)秀技術(shù)博客,經(jīng)常去讀一讀,以了解那些頂尖的博客作者們?cè)陉P(guān)注什么(最新的博客列表請(qǐng)參照pragmaticprogrammer.com)。
參加本地的用戶組活動(dòng)。Java,ruby,delphi,.NET,過(guò)程改進(jìn),面向?qū)ο笤O(shè)計(jì),Linux,Mac,以及其他的各種技術(shù)在很多地區(qū)都會(huì)有用戶組,聽(tīng)講座,然后積極加入到問(wèn)答環(huán)節(jié)中。
參加研討會(huì)議。計(jì)算機(jī)大會(huì)在世界各地舉行,許多知名的顧問(wèn)或者作者支持研討會(huì)或者課程。這些聚會(huì)時(shí)向?qū)<覍W(xué)習(xí)的最直接的好機(jī)會(huì)。
如饑似渴的閱讀。找一些關(guān)于軟件開(kāi)發(fā)和非技術(shù)主題的好書(shū)(我們很樂(lè)意為你推薦),也可以是一些專業(yè)的期刊和商業(yè)雜志,甚至是一些大眾媒體新聞(有趣的是在那些常常能看到老技術(shù)被吹捧為最新潮流)。
切身感受你能嗅到將要流行的新技術(shù),知道它們已經(jīng)發(fā)布或投入使用。如果必須要把工作切換到一種新的技術(shù)領(lǐng)域,你能做到。
平衡的技術(shù)· 許多新想法從末變得羽翼豐滿,成為有用的技術(shù)。即使是大型,熱門和資金充裕的項(xiàng)目,也會(huì)有同樣的下場(chǎng)。你要正確把握資金投入的精力。
· 你不可能精通每一項(xiàng)技術(shù),沒(méi)有必要去做這樣的嘗試。只要你在某些方面成為專家,就能使用同樣的方法,很容易地成為新領(lǐng)域的專家。
· 你要明白為什么需要這項(xiàng)新技術(shù)--------它視圖解決什么樣的問(wèn)題?它可以被用在什么地方?
· 避免在一時(shí)沖動(dòng)的情況下,只是因?yàn)橄雽W(xué)習(xí)而將應(yīng)用切換到新的技術(shù),框架或者開(kāi)發(fā)語(yǔ)言。在做決策之前,你必須評(píng)估新技術(shù)的優(yōu)勢(shì)。開(kāi)發(fā)一個(gè)小的原型系統(tǒng),是對(duì)付技術(shù)狂熱者的一劑良藥。
6.對(duì)團(tuán)隊(duì)投資
“不要和別人分享你的知識(shí)--------自己留著。你是因?yàn)檫@些知識(shí)而成為團(tuán)隊(duì)中的佼佼者,只要自己聰明就可以了,不用管其他失敗者。“團(tuán)隊(duì)中的開(kāi)發(fā)者們各有不同的能力、經(jīng)驗(yàn)和技術(shù)。每個(gè)人都各有所長(zhǎng)。不同才能和背景的人混在一起,是一個(gè)非常理想的學(xué)習(xí)環(huán)境。在一個(gè)團(tuán)隊(duì)中,如果只是你個(gè)人技術(shù)很好還遠(yuǎn)遠(yuǎn)不夠。如果其他團(tuán)隊(duì)成員的知識(shí)不夠,團(tuán)隊(duì)也無(wú)法發(fā)揮其應(yīng)有的作用:一個(gè)學(xué)習(xí)型的團(tuán)隊(duì)才是較好的團(tuán)隊(duì)。當(dāng)開(kāi)發(fā)項(xiàng)目的時(shí)候,你需要使用一些術(shù)語(yǔ)或者隱喻來(lái)清晰地傳達(dá)設(shè)計(jì)的概念和意圖。如果團(tuán)隊(duì)中的大部分成員不熟悉這些,就很難進(jìn)行高效地工作。再比如你參加了一個(gè)課程或者研討班之后,所學(xué)的知識(shí)如果不用,往往就會(huì)忘記。所以,你需要和其他團(tuán)隊(duì)成員分享所學(xué)的知識(shí),把這些知識(shí)引入團(tuán)隊(duì)中。找出你或團(tuán)隊(duì)中的高手擅長(zhǎng)的領(lǐng)域,幫助其他的團(tuán)隊(duì)成員在這些方面迎頭趕上(這樣做還有一個(gè)好處是,可以討論如何將這些東西應(yīng)用于自己的項(xiàng)目中)。“午餐會(huì)議”是在團(tuán)隊(duì)中分享知識(shí)非常好的方式。在一周之中挑選一天,例如星期三(一般來(lái)說(shuō)任何一天都可以,但最好不要是星期一和星期五)。事先計(jì)劃午餐時(shí)聚集在一起,這樣就不會(huì)擔(dān)心和其他會(huì)議沖突,也不需要特別的申請(qǐng)。為了降低成本,就讓大家自帶午餐。每周,要求團(tuán)隊(duì)中的一個(gè)人主持講座。他會(huì)給大家介紹一些概念,演示工具,或者做團(tuán)隊(duì)感興趣的任何一件事情。你可以挑一本書(shū),給大家說(shuō)說(shuō)其中一些特別內(nèi)容、項(xiàng)目或者實(shí)踐。①無(wú)論什么主題都可以。從每周主持講座的人開(kāi)始,先讓他講15分鐘,然后,進(jìn)行開(kāi)放式討論,這樣每個(gè)人都可以發(fā)表自己的意見(jiàn),討論這個(gè)主題對(duì)于項(xiàng)目的意義。討論應(yīng)該包括所能帶來(lái)的益處,提供來(lái)自自己應(yīng)用程序的示例,并準(zhǔn)備好聽(tīng)取進(jìn)一步的信息。這些午餐會(huì)議非常有用。它促進(jìn)了整個(gè)團(tuán)隊(duì)對(duì)這個(gè)行業(yè)的了解,你自己也可以從其他人身上學(xué)到很多東西。優(yōu)秀的管理者會(huì)重用那些能提高其他團(tuán)隊(duì)成員價(jià)值的人,因此這些活動(dòng)也直接有助于你的職業(yè)生涯。
提供你和團(tuán)隊(duì)學(xué)習(xí)的更好平臺(tái)。通過(guò)午餐會(huì)議可以增進(jìn)每個(gè)人的知識(shí)和技能,并幫助大家聚集在一起進(jìn)行溝通交流。喚起人們對(duì)技術(shù)和技巧的激情,將會(huì)對(duì)項(xiàng)目大有裨益。
切身感受這樣做,會(huì)讓每個(gè)人都覺(jué)得自己越來(lái)越聰明。整個(gè)團(tuán)隊(duì)都要了解新技術(shù),并指出如何使用它,或者指出需要注意的缺陷。
平衡的藝術(shù)
□ 讀書(shū)小組逐章一起閱讀一本書(shū),會(huì)非常有用,但是要選好書(shū)。《7天用設(shè)計(jì)模式和UML精通…》也許不會(huì)是一本好書(shū)。
□ 不是所有的講座都能引人入勝,有些甚至顯得很不合時(shí)宜。不管怎么樣,都要未雨綢繆;諾亞在建造方舟的時(shí)候,可并沒(méi)有開(kāi)始下雨,誰(shuí)能料到后來(lái)洪水泛濫呢?
□ 盡量讓講座走入團(tuán)隊(duì)中。如果午餐會(huì)議在禮堂中進(jìn)行,有餐飲公司供飯,還要使用幻燈片,那么就會(huì)減少大家接觸和討論的機(jī)會(huì)。
□ 堅(jiān)持有計(jì)劃有規(guī)律地舉行講座。持續(xù)、小步前進(jìn)才是敏捷。稀少、間隔時(shí)間長(zhǎng)的馬拉松式會(huì)議非敏捷也。
□ 如果一些團(tuán)隊(duì)成員因?yàn)槌晕顼埗毕?#xff0c;用美食引誘他們。
□ 不要局限于純技術(shù)的圖書(shū)和主題,相關(guān)的非技術(shù)主題(項(xiàng)目估算、溝通技巧等)也會(huì)對(duì)團(tuán)隊(duì)有幫助。
□ 午餐會(huì)議不是設(shè)計(jì)會(huì)議。總之,你贏專注討論那些與應(yīng)用相關(guān)的一般主題。具體的設(shè)計(jì)問(wèn)題,最好是留到設(shè)計(jì)會(huì)議中去解決。
7.懂得丟失
“那就是你一貫的工作方法,并且是有原因的,這個(gè)方法也很好地為你所用。開(kāi)始你就掌握了這個(gè)方法,很明顯它是最好的方法,真的,從那以后就不要再改變了。”
敏捷的根本之一就是擁抱變化。既然變化時(shí)永恒的,你有可能一直相同的技術(shù)和工具嗎?
不,不可能。我們一直在本章說(shuō)要學(xué)習(xí)新技術(shù)和進(jìn)方法。但是記住,你也需要學(xué)會(huì)如何丟棄。
隨著科技進(jìn)步,曾經(jīng)非常有用的東西往往會(huì)靠邊站。它們不再有用了,它們還會(huì)降低你的效率。當(dāng)Andy第一次編程的時(shí)候,內(nèi)存占用時(shí)一個(gè)大問(wèn)題。你通常無(wú)法再主存儲(chǔ)器(大約48KB)中一次裝載整個(gè)程序,所以必須把程序切分成塊。當(dāng)一個(gè)程序塊交換進(jìn)去的時(shí)候,其他一些程序必須出來(lái),并且你無(wú)法再一個(gè)塊中調(diào)用另一個(gè)塊中的函數(shù)。
正是這個(gè)實(shí)際約束,極大地影響了你的設(shè)計(jì)和編程技術(shù)。
想想在過(guò)去,面對(duì)處理器之外的循環(huán)操作,你必須花費(fèi)很大的精力去手動(dòng)調(diào)整匯編語(yǔ)言的編譯輸出。可以想象,如是是使用JavaScript或者J2EE代碼,你還需要這么干嗎?
對(duì)于大多數(shù)的商業(yè)應(yīng)用,技術(shù)已經(jīng)有了巨大的變化,不再像以前那樣,處處考慮內(nèi)存占用、手動(dòng)的重復(fù)占位及手工調(diào)整匯編語(yǔ)言。但我們?nèi)匀豢吹胶芏嚅_(kāi)發(fā)者從未丟棄這些習(xí)慣。
Andy曾經(jīng)看到過(guò)這樣一段C語(yǔ)言代碼:一個(gè)大的for循環(huán),循環(huán)里面的代碼一共輸出了60頁(yè)。那個(gè)作者“不相信”編譯器的優(yōu)化,所以決定自己手動(dòng)實(shí)現(xiàn)循環(huán)體展開(kāi)和其他一些技巧。我們只能祝愿維護(hù)那一大堆代碼的人好運(yùn)。
在過(guò)去,這段的挨罵也許可以勉強(qiáng)接受。但是,現(xiàn)在絕對(duì)不可以了。電腦和 CPU曾經(jīng)非常昂貴,而現(xiàn)在它們就是日用品。現(xiàn)在,開(kāi)發(fā)者的時(shí)間才是緊缺和昂貴的資源。
這樣的轉(zhuǎn)變?cè)诰徛剡M(jìn)行著,但是人們也真正認(rèn)清了這個(gè)事實(shí)。我們看到,需要耗費(fèi)10人年開(kāi)發(fā)的J2EE項(xiàng)目已經(jīng)從輝煌走向下坡路。使用PHP,一個(gè)月的時(shí)間就可以完成,并能交付大部分的功能。想PHP這樣的語(yǔ)言或Ruby on Rails 這樣的框架越來(lái)越受關(guān)注(參見(jiàn)[TH05]),這表明了開(kāi)發(fā)者已經(jīng)意識(shí)到舊的技術(shù)再也行不通了。
但丟棄已經(jīng)會(huì)的東西并不容易。很多團(tuán)隊(duì)在猶豫,是因?yàn)楣芾碚呔芙^用500美元購(gòu)買一臺(tái)構(gòu)建愛(ài)你機(jī)器(build machine),卻寧愿花費(fèi)好幾萬(wàn)美元的人工費(fèi),讓程序員花時(shí)間找出問(wèn)題。而實(shí)際上,買一臺(tái)構(gòu)建機(jī)器就可以解決這些問(wèn)題。如果購(gòu)買機(jī)器需要花費(fèi)500 000美元,那樣做還情有可原,但現(xiàn)在早已時(shí)過(guò)境遷了。
在學(xué)習(xí)一門新技術(shù)的時(shí)候,多問(wèn)問(wèn)自己,是否把太多舊的態(tài)度和方法用在了新技術(shù)上。學(xué)習(xí)面向?qū)ο缶幊毯蛯W(xué)習(xí)面向?qū)ο筮^(guò)程是截然不同的。很容易會(huì)發(fā)現(xiàn)有人用C語(yǔ)言的方式編寫(xiě)Java代碼,用VB的方式編寫(xiě)C#的代碼(或者用Fortran的方式做任何事情)。這樣,你辛苦地轉(zhuǎn)向一門新的語(yǔ)言,卻失去了期望獲得的益處。
打破舊習(xí)慣很難,更難的是自己還沒(méi)有意識(shí)到這個(gè)問(wèn)題。丟棄的第一步,就是要意識(shí)到你還在使用過(guò)時(shí)的方法,這也是很難的部分。另一個(gè)難點(diǎn)就是要做到真正地丟棄舊習(xí)慣。思維定式是經(jīng)過(guò)多年摸爬滾打才構(gòu)建成型的,已經(jīng)根深蒂固,沒(méi)有人可以很容易就丟棄它們。
這也不是說(shuō)你真的要完全丟棄它們。前面那個(gè)內(nèi)存重復(fù)占位的例子,只是在稍大緩存中用手工維護(hù)一組工件的特殊案例。盡管實(shí)現(xiàn)方式不同了,但以前的技術(shù)還在你的大腦中。你不可能撬開(kāi)大腦,把這一段極易神經(jīng)剪掉。其實(shí),根據(jù)具體情況還可以運(yùn)用舊知識(shí)。如果環(huán)境合適,可以舉一反三地靈活應(yīng)用,但一定要保證不是習(xí)慣性地落入舊習(xí)慣。
應(yīng)該力求盡可能完全轉(zhuǎn)入新的開(kāi)發(fā)環(huán)境。例如,學(xué)習(xí)一門新的編程語(yǔ)言時(shí),應(yīng)使用推薦的集成開(kāi)發(fā)環(huán)境,而不是你過(guò)去開(kāi)發(fā)時(shí)用的工具插件。用這個(gè)工具編寫(xiě)一個(gè)和過(guò)去完全不同類型的項(xiàng)目。轉(zhuǎn)換的時(shí)候,完全不要使用過(guò)去的語(yǔ)言開(kāi)發(fā)工具。只有更少被舊習(xí)慣牽絆,才更容易養(yǎng)成新習(xí)慣。
學(xué)習(xí)新的東西,丟棄舊的東西。在學(xué)習(xí)一門新技術(shù)的時(shí)候,要丟棄會(huì)阻止你前進(jìn)的舊習(xí)慣。畢竟,汽車要比馬車車廂強(qiáng)得多。
切身感受
新技術(shù)會(huì)讓人感到有一點(diǎn)恐懼。你確實(shí)需要學(xué)習(xí)很多東西。已有的技能和習(xí)慣為你打下很好的基礎(chǔ),但不能依賴它們。
平衡的藝術(shù)
□沉舟側(cè)畔千帆過(guò),病樹(shù)前頭萬(wàn)木春。要果斷丟棄舊習(xí)慣,一味遵循過(guò)時(shí)的舊習(xí)慣會(huì)危害你的職業(yè)
生涯。
□不是完全忘記舊的習(xí)慣,而是只在使用適當(dāng)?shù)募夹g(shù)時(shí)才使用它。
□對(duì)于所使用的語(yǔ)言,要總結(jié)熟悉的語(yǔ)言特性,并且比較這些特性在新語(yǔ)言或新版本中有什么
不同。
8.打破砂鍋問(wèn)道底
“接受別人給的解釋。別人告訴你問(wèn)題出在了什么地方,你就去看什么地方。不需要再浪費(fèi)時(shí)間去追根究底。”
前面談到的一些習(xí)慣是關(guān)于如何提高你和團(tuán)隊(duì)的技術(shù)的。下面有一個(gè)習(xí)慣幾乎總是有用的,可以用于設(shè)計(jì)、調(diào)試以及理解需求。
假設(shè),應(yīng)用系統(tǒng)出了大問(wèn)題,它們找你來(lái)修復(fù)它。但你不熟悉這個(gè)應(yīng)用系統(tǒng),所以它們會(huì)幫助你,告訴你問(wèn)題一定是出在那個(gè)特殊的模塊中——你可以放心地忽略應(yīng)用系統(tǒng)的其他地方。你必須很快地解決這個(gè)問(wèn)題,因?yàn)楦愫献鞯倪@些人耐心也很有限。
當(dāng)你受到那些壓力的時(shí)候,也許會(huì)覺(jué)得受到了脅迫,不想去深入了解問(wèn)題,而且別人告訴你的已經(jīng)深入了。然而,為了解決問(wèn)題,你需要很好地了解系統(tǒng)的全局。你需要產(chǎn)看所有你認(rèn)為霍問(wèn)題相關(guān)的部分——即使他人覺(jué)得這并不相干。
觀察一下醫(yī)生是如何工作的,當(dāng)你不舒服的時(shí)候,醫(yī)生會(huì)問(wèn)你各種各樣的問(wèn)題——你有什么習(xí)慣,你吃了什么東西,什么地方疼痛,你已經(jīng)服過(guò)什么藥等。人的身體非常復(fù)雜,會(huì)受到很多因素的影響。如果醫(yī)生沒(méi)有全面地了解狀況,就很可能出現(xiàn)誤診。
例如,住在紐約的一個(gè)病人患有高燒、皮疹、嚴(yán)重的頭痛、眼睛后面疼痛,以及肌肉或關(guān)節(jié)疼痛,它也許是染上了流感或者麻疹。但是,通過(guò)全面的檢查,醫(yī)生發(fā)現(xiàn)這個(gè)倒霉的病人剛?cè)ツ厦乐薅燃倩貋?lái)。所以,這病也許并不是簡(jiǎn)單的流感,還有可能是在新大陸染上的熱帶傳染病登革熱。
在計(jì)算機(jī)世界中葉很相似,很多問(wèn)題都會(huì)影響你的應(yīng)用系統(tǒng)。為了解決問(wèn)題,你需要知道許多可能的影響因素。當(dāng)找人詢問(wèn)任何相關(guān)的問(wèn)題時(shí),讓他們耐心地回答你的問(wèn)題,這是你的職責(zé)。
或者,假設(shè)你和資深的開(kāi)發(fā)者一起工作。他們有可能比你更了解這個(gè)系統(tǒng)。但他們也是人,有時(shí)他們也會(huì)忘記一些東西。你的問(wèn)題甚至?xí)椭矫骼砬逅悸贰D銖囊粋€(gè)新人角度提出的問(wèn)題,給他們提供了一個(gè)新的視角,也許就幫助他們解決了一直令人困擾的問(wèn)題。
“為什么”是一個(gè)非常好的問(wèn)題,事實(shí)上,在一本流行的管理圖書(shū)《第五項(xiàng)修煉》中,作者建議,在理解一個(gè)問(wèn)題的時(shí)候,需要漸次地問(wèn)5個(gè)以上的“為什么”。這聽(tīng)起來(lái)就像退回到了4歲,那時(shí)對(duì)一切都充滿著好奇。它是很好的方式,進(jìn)一步挖掘簡(jiǎn)單直白的答案,通過(guò)這個(gè)路線,設(shè)想就會(huì)更加接近事實(shí)真相。
在《第五項(xiàng)修煉》一書(shū)中就有這樣的例子。咨詢師訪問(wèn)一個(gè)制造設(shè)備工廠的經(jīng)理,就用到了這樣一個(gè)追根究底的分析。看到地板上有油漬的時(shí)候,經(jīng)理的第一反應(yīng)是命令工人把它打掃干凈。但是,咨詢師問(wèn):“為什么地板上有油漬?”經(jīng)理不熟悉整個(gè)流程,就會(huì)責(zé)備這是清潔隊(duì)的疏忽。咨詢師再問(wèn):“為什么地板上有油漬?”通過(guò)一系列漸次提出的“為什么”和許多不同部門員工的幫助,咨詢師最后找到了真正的問(wèn)題所在“采購(gòu)政策表述不明確,導(dǎo)致大量采購(gòu)了一批有缺陷的墊圈。
答案出來(lái)之后,經(jīng)理和其他員工都十分震驚,他們對(duì)這事一無(wú)所知。由此發(fā)現(xiàn)了一個(gè)重大的隱患,避免了其他方面更大的損失。而咨詢師所做的不過(guò)就是問(wèn)了“為什么“。
“哎呀,只要每周重啟一次系統(tǒng),就沒(méi)有問(wèn)題了。”真的嗎?為什么呀?“你必須依次執(zhí)行3次構(gòu)建才能完成構(gòu)建。” 真的嗎?為什么呀?“我們的用戶根本不想要那個(gè)功能” 真的嗎?為什么呀?
為什么呀?
不停的問(wèn)為什么。不能只滿足于別人告訴你的表面現(xiàn)象,要不停地提問(wèn)知道你明白問(wèn)題的根源。
切身感受
這就好比從礦石中采掘貴重的珠寶。你不停地篩選掉無(wú)關(guān)的物質(zhì),一次比一次深入,直到找到發(fā)光的寶石。你要能感覺(jué)到真正地理解問(wèn)題,而不是只知道表面的癥狀。
平衡的藝術(shù)
□你可能會(huì)跑題,問(wèn)了一些與主題無(wú)關(guān)的問(wèn)題。就好比是,如果汽車啟動(dòng)了,你問(wèn)是不是輪胎出了問(wèn)題,就是沒(méi)有任何幫助的。問(wèn)“為什么”,但是要問(wèn)道點(diǎn)子上。
□當(dāng)你問(wèn)“為什么”的時(shí)候,也許你會(huì)被反問(wèn):“為什么你問(wèn)這個(gè)問(wèn)題?”在提問(wèn)之前,想好你提問(wèn)的理由,這會(huì)有助于你問(wèn)出恰當(dāng)?shù)膯?wèn)題。
□“這個(gè),我不知道”是一個(gè)好的起點(diǎn),應(yīng)該由此進(jìn)行更進(jìn)一步的調(diào)查。而不是在此戛然結(jié)束。
9.把握開(kāi)發(fā)節(jié)奏
“我們很長(zhǎng)時(shí)間沒(méi)有進(jìn)行代碼復(fù)審,所以這周會(huì)復(fù)審所有的代碼。此外,我們也要做一個(gè)發(fā)布計(jì)劃了,那就從星期二開(kāi)始,用3周時(shí)間做下一個(gè)發(fā)布計(jì)劃。”
在許多不成功的項(xiàng)目中,基本上都是隨意安排工作計(jì)劃,沒(méi)有任何的規(guī)律。那樣的隨機(jī)安排很難處理。你根本不知道明天將會(huì)發(fā)生什么,也不知道什么時(shí)候開(kāi)始下一輪的全休“消防演習(xí)”。
但是,敏捷項(xiàng)目會(huì)有一個(gè)節(jié)奏和循環(huán),讓開(kāi)發(fā)更加輕松。例如,Scrum約定了30天之內(nèi)不應(yīng)該發(fā)生需求變化,這樣確保團(tuán)隊(duì)有一個(gè)良性的開(kāi)發(fā)節(jié)奏。這有助于防止一次計(jì)劃太多的工作和一些過(guò)大的需求變更。
相反,很多敏捷實(shí)踐必須一直進(jìn)行,也就是說(shuō),它貫穿于項(xiàng)目的整個(gè)生命周期。有人說(shuō),上帝發(fā)明了時(shí)間,就是為了防止所有事情同時(shí)發(fā)生。因此我們需要更具遠(yuǎn)見(jiàn),保持不同的開(kāi)發(fā)節(jié)奏,這樣敏捷項(xiàng)目的所有事情就不會(huì)突然同時(shí)發(fā)生,也不會(huì)隨機(jī)發(fā)生,時(shí)間也不會(huì)不可預(yù)知。
我們先來(lái)看某個(gè)工作日的情況。你希望每天工作結(jié)束的時(shí)候,都能完成自己的工作,你手上沒(méi)有遺留下任何重要的任務(wù)。當(dāng)然,每天都能這樣是不現(xiàn)實(shí)的。但是,你可以做到在每天下班離開(kāi)公司前運(yùn)行測(cè)試,并提交一天完成的代碼。如果已經(jīng)很晚了,并且你只是嘗試性地編寫(xiě)了一些代碼,那么也許最好應(yīng)該刪掉這些代碼,第二天從頭開(kāi)始。
這個(gè)建議聽(tīng)起來(lái)十分極端,也許確實(shí)有一點(diǎn)。①但是如果你正在開(kāi)發(fā)小塊的任務(wù),這個(gè)方式非常有助于你管理自己的時(shí)間:如果在你工作的時(shí)候沒(méi)有一個(gè)固定的最終期限(例如一天的結(jié)束),就應(yīng)該好好想想了。它會(huì)讓你的工作有一個(gè)節(jié)奏,在每天下班的時(shí)候,提交所有的工作,開(kāi)心收工。這樣,明天就能開(kāi)始新的內(nèi)容,解決下一系列難題。
站立會(huì)議(習(xí)慣38,第148頁(yè))最好每天在固定的時(shí)間和地點(diǎn)舉行,比如說(shuō)上午10點(diǎn)左右。要養(yǎng)成這樣的習(xí)慣,在那時(shí)就準(zhǔn)備好一切參加站立會(huì)議。
最大的節(jié)拍就是迭代時(shí)間(習(xí)慣17,第69頁(yè)),一般是1~4周的時(shí)間。不管你的一個(gè)迭代是多長(zhǎng),都應(yīng)該堅(jiān)持-----確保每個(gè)迭代周期的時(shí)間相同很重要。運(yùn)用有規(guī)律的開(kāi)發(fā)節(jié)奏,會(huì)更容易達(dá)到目標(biāo),并確保項(xiàng)目不停地前進(jìn)。
解決 ,在事情變得一團(tuán)糟之前。保持事件之間穩(wěn)定重復(fù)的間隔,更容易解決常見(jiàn)的重復(fù)任務(wù)。
切身感受項(xiàng)目開(kāi)發(fā)需要有一致和穩(wěn)定的節(jié)奏。編輯,運(yùn)行測(cè)試,代碼復(fù)審,一致的迭代,然后發(fā)布。如果知道什么時(shí)候開(kāi)始下一個(gè)節(jié)拍,跳舞就會(huì)更加容易。
平衡的藝術(shù)
· 在每天結(jié)束的時(shí)候,測(cè)試代碼,提交代碼,沒(méi)有殘留的代碼。
· 不要搞得經(jīng)常加班。
· 已固定、有規(guī)律的長(zhǎng)度運(yùn)行迭代(第69頁(yè),習(xí)慣17)。也許剛開(kāi)始你要調(diào)整迭代的長(zhǎng)度,找到團(tuán)隊(duì)最舒服可行的時(shí)間值,但之后就必須要堅(jiān)持。
· 如果開(kāi)發(fā)節(jié)奏過(guò)于密集,你會(huì)精疲力竭的。一般來(lái)說(shuō),當(dāng)與其他團(tuán)隊(duì)(或組織)合作時(shí),你需要減慢開(kāi)發(fā)節(jié)奏。因此人們常說(shuō),互聯(lián)網(wǎng)時(shí)代發(fā)展太快,有害健康。
· 有規(guī)律的開(kāi)發(fā)節(jié)奏會(huì)暴露很多問(wèn)題,讓你有更多鼓起勇氣的借口(第23頁(yè),習(xí)慣4)。
· 就像是減肥一樣,一點(diǎn)點(diǎn)的成功也是一個(gè)很大的激勵(lì)。小而可達(dá)到的目標(biāo)會(huì)讓每個(gè)人全速前進(jìn)。慶祝每一次難忘的成功:共享美食和啤酒或者團(tuán)隊(duì)聚餐。
第四章 交付用戶想要的軟件
沒(méi)有任何計(jì)劃在遇敵后還能繼續(xù)執(zhí)行。---------- Helmuth von Moltke(德國(guó)陸軍元帥,1848—1916)
客戶把需求交給你了,要你幾年后交付這個(gè)系統(tǒng)。然后,你就基于這些需求構(gòu)建客戶需要的系統(tǒng),最后按時(shí)交付。客戶看到了軟件,連聲稱贊做得好。從此你又多了一個(gè)忠實(shí)客戶,接著你很開(kāi)心地進(jìn)入了下一個(gè)項(xiàng)目。你的項(xiàng)目通常都是這樣運(yùn)作的,是這樣的嗎?
其實(shí),大部分人并不會(huì)遇到這樣的項(xiàng)目。通常情況是:客戶最后看到了軟件,要么震驚要么不高興。他們不喜歡所看到的軟件,他們認(rèn)為很多地方需要修改。他們要的功能不在他們給你的原始需求文檔中。這聽(tīng)起來(lái)是不是更具有代表性?
Helmuth von Moltke曾說(shuō)過(guò):“沒(méi)有任何計(jì)劃在遇敵后還能繼續(xù)執(zhí)行。”我們的敵人不是客戶,不是用戶,不是隊(duì)友,也不是管理者。真正的敵人是變化。軟件開(kāi)發(fā)如戰(zhàn)爭(zhēng),形勢(shì)的變化快速而又劇烈。固守昨天的計(jì)劃而無(wú)視環(huán)境的變化會(huì)帶來(lái)災(zāi)難。你不可能“戰(zhàn)勝”變化—無(wú)論它是設(shè)計(jì)、架構(gòu)還是你對(duì)需求的理解。敏捷—成功的軟件開(kāi)發(fā)方法—取決于你識(shí)別和適應(yīng)變化的能力。只有這樣才有可能在預(yù)算之內(nèi)及時(shí)完成開(kāi)發(fā),創(chuàng)建真正符合用戶需求的系統(tǒng)。
在本章中,我們會(huì)介紹如何達(dá)到敏捷的目標(biāo)。首先,要介紹為什么用戶和客戶參與開(kāi)發(fā)如此重要,以及為什么讓客戶做決定(從第45頁(yè)開(kāi)始)。設(shè)計(jì)是軟件開(kāi)發(fā)的基礎(chǔ),沒(méi)有它很難做好開(kāi)發(fā),但你也不能被它牽制。從第48頁(yè)開(kāi)始,將介紹如何讓設(shè)計(jì)指導(dǎo)而不是操縱開(kāi)發(fā)。說(shuō)到牽制,你應(yīng)確保在項(xiàng)目中引入合適的技術(shù)。你需要合理地使用技術(shù)(第52頁(yè)介紹)。
為了讓軟件符合用戶的需求,要一直做下面的準(zhǔn)備工作。為了降低集成新代碼帶來(lái)的破壞性變化,你要提早集成,頻繁集成(第58頁(yè))。當(dāng)然,你不想破壞已有的代碼,想讓代碼一直保持可以發(fā)布(從第55頁(yè)開(kāi)始)。
你不能一次又一次為用戶演示新功能,而浪費(fèi)寶貴的開(kāi)發(fā)時(shí)間,因此你需要提早實(shí)現(xiàn)自動(dòng)化部署(第61頁(yè))。只要你的代碼一直可用,并且易于向用戶部署,你就能使用演示獲得頻繁反饋(第64頁(yè))。這樣你就能經(jīng)常向全世界發(fā)布新版本。你想通過(guò)使用短迭代,增量發(fā)布來(lái)幫助經(jīng)常發(fā)布新功能,與用戶的需求變化聯(lián)系更緊密(從第69頁(yè)開(kāi)始介紹它)。
最后,特別是客戶要求預(yù)先簽訂固定價(jià)格合約時(shí),很難通過(guò)敏捷的方法讓客戶與我們同坐一條船上。而且,事實(shí)上是固定的價(jià)格就意味著背叛承諾,我們會(huì)在第73頁(yè)了解如何處理這種情況。
10.讓客戶做決定
“開(kāi)發(fā)者兼具創(chuàng)新和智慧,最了解應(yīng)用程序。因此,所有關(guān)鍵決定都應(yīng)該由開(kāi)發(fā)者定奪。每次業(yè)務(wù)人員介入的時(shí)候,都會(huì)弄得一團(tuán)槽,他們無(wú)法理解我們做事的邏輯。”
在設(shè)計(jì)方面,做決定的時(shí)候必須有開(kāi)發(fā)者參與。可是,在一個(gè)項(xiàng)目中,他們不應(yīng)該做所有的決定,特別是業(yè)務(wù)方面的決定。
就拿項(xiàng)目經(jīng)理Pat的例子來(lái)說(shuō)吧。Pat的項(xiàng)目是遠(yuǎn)程開(kāi)發(fā),一切按計(jì)劃且在預(yù)算內(nèi)進(jìn)行著—就像是個(gè)可以寫(xiě)入教科書(shū)的明星項(xiàng)目。Pat高高興興地把代碼帶到客戶那里,給客戶演示,卻敗興而歸。
原來(lái),Pat的業(yè)務(wù)分析師沒(méi)有和用戶討論,而是自作主張,決定了所有的問(wèn)題。在整個(gè)開(kāi)發(fā)過(guò)程中,企業(yè)主根本沒(méi)有參與低級(jí)別的決策。項(xiàng)目離完成還早著呢,就已經(jīng)不能滿足用戶的需要了。這個(gè)項(xiàng)目一定會(huì)延期,又成為一個(gè)經(jīng)典的失敗案例。
因而,你只有一個(gè)選擇:要么現(xiàn)在就讓用戶做決定,要么現(xiàn)在就開(kāi)始開(kāi)發(fā),遲些讓用戶決定,不過(guò)要付出較高的成本。如果你在開(kāi)發(fā)階段回避這些問(wèn)題,就增加了風(fēng)險(xiǎn),但是你要能越早解決這些問(wèn)題,就越有可能避免繁重的重新設(shè)計(jì)和編碼。甚至在接近項(xiàng)目最終期限的時(shí)候,也能避免與日俱增的時(shí)間壓力。
例如,假設(shè)你要完成一個(gè)任務(wù),有兩種實(shí)現(xiàn)方式。第一種方式的實(shí)現(xiàn)比較快,但是對(duì)用戶有一點(diǎn)限制。第二種方式實(shí)現(xiàn)起來(lái)需要更多的時(shí)間,但是可以提供更大的靈活性。很顯然,你有時(shí)間的壓力(什么項(xiàng)目沒(méi)有時(shí)間壓力呢),那么你就用第一種很快的方式嗎?你憑什么做出這樣的決定呢?是投硬幣嗎?你詢問(wèn)了同事或者你的項(xiàng)目經(jīng)理嗎?
作者之一Venkat最近的一個(gè)項(xiàng)目就遇到了類似的問(wèn)題。項(xiàng)目經(jīng)理為了節(jié)約時(shí)間,采取了第一種方式。也許你會(huì)猜到,在Beta版測(cè)試的時(shí)候,軟件暴露出的局限讓用戶震驚,甚至憤怒。結(jié)果還得重做,花費(fèi)了團(tuán)隊(duì)更多的金錢、時(shí)間和精力。
開(kāi)發(fā)者(及項(xiàng)目經(jīng)理)能做的一個(gè)最重要的決定就是:
判斷哪些是自己決定不了的,應(yīng)該讓企業(yè)主做決定。你
不需要自己給業(yè)務(wù)上的關(guān)鍵問(wèn)題做決定。畢竟,那不是
你的事情,如果遇到 了一個(gè)問(wèn)題,會(huì)影響到系統(tǒng)的行為
或者如何使用系統(tǒng),把這個(gè)問(wèn)題告訴業(yè)務(wù)負(fù)責(zé)人。如果項(xiàng)目領(lǐng)導(dǎo)或經(jīng)理試圖全權(quán)負(fù)責(zé)這些問(wèn)題,要委婉地勸說(shuō)他們,這些問(wèn)題最好還是和真正的業(yè)務(wù)負(fù)責(zé)人或者客戶商議(見(jiàn)習(xí)慣4,第23頁(yè))。
當(dāng)你和客戶討論問(wèn)題的時(shí)候,準(zhǔn)備好幾種可選擇的方案。不是從技術(shù)的角度,而是從業(yè)務(wù)的角度,介紹每種方案的優(yōu)缺點(diǎn),以及潛在的成本和利益。和他們討論每個(gè)選擇對(duì)時(shí)間和預(yù)算的影響,以及如何權(quán)衡。無(wú)論他們作出了什么決定,他們必須接受它,所以最好讓他們了解一切之后再做這些決定。如果事后他們又想要其他的東西,可以公正地就成本和時(shí)間重新談判。
畢竟,這是他們的決定。
讓你的客戶做決定。開(kāi)發(fā)者、經(jīng)理或者業(yè)務(wù)分析師不應(yīng)該做業(yè)務(wù)方面的決定。用業(yè)務(wù)負(fù)責(zé)人能夠理解的言語(yǔ),向他們?cè)敿?xì)解釋遇到的問(wèn)題,并讓他們做決定。
切身感受業(yè)務(wù)應(yīng)用需要開(kāi)發(fā)者和業(yè)務(wù)負(fù)責(zé)人互相配合來(lái)開(kāi)發(fā)。這種配合的感覺(jué)就應(yīng)該像一種良好的、誠(chéng)實(shí)的工作關(guān)系。
平衡的藝術(shù)
第3章 記錄客戶做出的決定,并注明原因。好記性不如爛筆頭。可以使用工程師的工作日記或日志、Wiki、郵件記錄或者問(wèn)題跟蹤數(shù)據(jù)庫(kù)。但是也要注意,你選擇的記錄方法不能太笨重或者太繁瑣。
第4章 不要用低級(jí)別和沒(méi)有價(jià)值的問(wèn)題打擾繁忙的業(yè)務(wù)人員。如果問(wèn)題對(duì)他們的業(yè)務(wù)沒(méi)有影響,就應(yīng)該是沒(méi)有價(jià)值的。
第5章 不要隨意假設(shè)低級(jí)別的問(wèn)題不會(huì)影響他們的業(yè)務(wù)。如果能影響他們的業(yè)務(wù),就是有價(jià)值的問(wèn)題。
第6章 如果業(yè)務(wù)負(fù)責(zé)人回答“我不知道”,這也是一個(gè)稱心如意的答案。也許是他們還沒(méi)有想到那么遠(yuǎn),也許是他們只有看到運(yùn)行的實(shí)物才能評(píng)估出結(jié)果。盡你所能為他們提供建議,實(shí)現(xiàn)代碼的時(shí)候也要考慮可能出現(xiàn)的變化。
11.讓設(shè)計(jì)指導(dǎo)而不是操縱開(kāi)發(fā)
“設(shè)計(jì)文檔應(yīng)該盡可能詳細(xì),這樣,低級(jí)的代碼工人只要敲入代碼就可以了。在高層方面,詳細(xì)描述對(duì)象的關(guān)聯(lián)關(guān)系;在低層方面,詳細(xì)描述對(duì)象之間的交互。其中一定要包括方法的實(shí)現(xiàn)信息和參數(shù)的注釋。也不要忘記給出類里面的所有字段。編寫(xiě)代碼的時(shí)候,無(wú)論你發(fā)現(xiàn)了什么,絕不能偏離了設(shè)計(jì)文檔。“
“設(shè)計(jì)“是軟件開(kāi)發(fā)過(guò)程不可缺少的步驟。它幫助你理解系統(tǒng)的細(xì)節(jié),理解部件和子系統(tǒng)之間的關(guān)系,并且指導(dǎo)你的實(shí)現(xiàn)。一些成熟的方法論很強(qiáng)調(diào)設(shè)計(jì),例如,統(tǒng)一過(guò)程(Unified Process,UP)十分重視和產(chǎn)品相關(guān)的文檔。項(xiàng)目管理者和企業(yè)主常常為開(kāi)發(fā)細(xì)節(jié)困擾,他們希望在開(kāi)始編碼之前,先有完整的設(shè)計(jì)和文檔。畢竟,那也是你如何管理橋梁或建筑項(xiàng)目的,難道不是嗎?
另一方面,敏捷方法建議你早在開(kāi)發(fā)初期就開(kāi)始編碼。是否那就意味著沒(méi)有設(shè)計(jì)呢?不,絕對(duì)不是,好的設(shè)計(jì)仍然十分重要。畫(huà)關(guān)鍵工作圖(例如,用UML)是不比可少的,因?yàn)?#xff0c;要使用類極其交互關(guān)系來(lái)描繪系統(tǒng)是如何組織的。在做設(shè)計(jì)的時(shí)候,你需要花時(shí)間去思考(討論)各種不同選擇的缺陷和益處,以及如何做權(quán)衡。
然后,下一步才考慮是否需要開(kāi)始編碼。如果你在前期沒(méi)有考慮清楚這些問(wèn)題,就草草地開(kāi)始編碼,很可能會(huì)被很多意料之處的問(wèn)題搞暈。甚至在建筑工程方面也有類似的情況。在鋸一根木頭的時(shí)候,通常的做法就是先鋸一塊必須要稍微長(zhǎng)一點(diǎn)的木塊,最后細(xì)致地修整,直到它正好符合需求。
但是,即使之前已經(jīng)提交了設(shè)計(jì)文檔,也還會(huì)有一些意料之外的情況出現(xiàn)。時(shí)刻謹(jǐn)記,此階段提出的設(shè)計(jì)只是基于你目前對(duì)需求的理解而已。一旦開(kāi)始了編碼,一切都會(huì)改變。設(shè)計(jì)及其代碼實(shí)現(xiàn)會(huì)不停地發(fā)展和變化。
一些項(xiàng)目領(lǐng)導(dǎo)和經(jīng)理認(rèn)為設(shè)計(jì)應(yīng)該盡可能地詳細(xì),這樣就可以簡(jiǎn)單地交付給“代碼工人們”。他們認(rèn)為代碼工人不需要做任何決定,只要簡(jiǎn)單地把設(shè)計(jì)轉(zhuǎn)化成代碼就可以了。就作者本人而言,沒(méi)有一個(gè)愿意在這樣的團(tuán)隊(duì)中做純粹的打字員。我們猜想你也不愿意。
如果設(shè)計(jì)師門把自己的想法繪制成
精美的文檔,然后 把它們?nèi)咏o程序員
去編碼,那會(huì)發(fā)生什么(查閱習(xí)慣39,
在第152頁(yè))?程序員會(huì)在壓力下,
完全按照設(shè)計(jì)或者圖畫(huà)的樣子編碼。如果系統(tǒng)和已有代碼的現(xiàn)狀表明接收到的設(shè)計(jì)不夠理想,那該怎么辦?在糟糕了!時(shí)間已經(jīng)花費(fèi)在設(shè)計(jì)上,沒(méi)有功夫回頭重新設(shè)計(jì)了。團(tuán)隊(duì)會(huì)死撐下去,用代碼實(shí)現(xiàn)了明明知道是錯(cuò)誤的設(shè)計(jì)。這聽(tīng)起來(lái)是不是很愚蠢?是夠愚蠢的,但是有一些公司真的就是這樣做的。
嚴(yán)格的需求-設(shè)計(jì)-代碼-測(cè)試開(kāi)發(fā)流程源于理想化的瀑布式開(kāi)發(fā)方法,它導(dǎo)致在前面進(jìn)行了過(guò)度的設(shè)計(jì)。這樣在項(xiàng)目的生命周期中,更新和維護(hù)這些詳細(xì)的設(shè)計(jì)文檔變成了主要工作,需要時(shí)間和資源方面的巨大投資,卻只是有很少的回報(bào)。我們本可以做得更好。
設(shè)計(jì)可以分為兩層:戰(zhàn)略和戰(zhàn)術(shù),前期的設(shè)計(jì)屬于戰(zhàn)略,通常只有在沒(méi)有深入理解需求的時(shí)候需要這樣的設(shè)計(jì)。更確切地說(shuō),它應(yīng)該只描述總體戰(zhàn)略,不應(yīng)深入到具體的細(xì)節(jié)。
前面剛說(shuō)過(guò),戰(zhàn)略級(jí)別的設(shè)計(jì)不應(yīng)該具體說(shuō)明程序方法、參數(shù)、字段和對(duì)象交互精確順序的細(xì)節(jié)。那應(yīng)該留到戰(zhàn)術(shù)設(shè)計(jì)階段,它應(yīng)該在項(xiàng)目開(kāi)發(fā)的時(shí)候再具體展開(kāi)。
良好戰(zhàn)略設(shè)計(jì)應(yīng)該扮演地圖的角色,
指引你向正確的方向前進(jìn)。任何設(shè)計(jì)
僅是一個(gè)起跑點(diǎn):它就像你的代碼一
樣,在項(xiàng)目的生命周期中,會(huì)不停地進(jìn)一步發(fā)展和提煉。
下面的故事會(huì)給我們一些啟發(fā)。在1804年,Lewis與Clark進(jìn)行了橫穿美國(guó)的壯舉,他們的“設(shè)計(jì)“就是穿越蠻荒。但是,他們不知道在穿越殖民地時(shí)會(huì)遇到什么樣的問(wèn)題。他們只知道自己的目標(biāo)和制約條件,但是不知道旅途的細(xì)節(jié)。
軟件項(xiàng)目中的設(shè)計(jì)也與此類似。在沒(méi)有穿越殖民地的時(shí)候,你不可能知道會(huì)出現(xiàn)什么情況。所以,不要事先浪費(fèi)時(shí)間規(guī)劃如何徒步穿越河流,只有當(dāng)你走到河岸邊的時(shí)候,才能真正評(píng)估和規(guī)劃如何穿越。只有到那時(shí),你才開(kāi)始真正的戰(zhàn)術(shù)設(shè)計(jì)。
不要一開(kāi)始就進(jìn)行戰(zhàn)術(shù)設(shè)計(jì),它的重點(diǎn)是集中在單個(gè)的方法或數(shù)據(jù)類型上。這時(shí),更適合討論如何設(shè)計(jì)類的職責(zé)。因?yàn)檫@仍然是一個(gè)高層次、面向目標(biāo)的設(shè)計(jì),事實(shí)上,CRC(類-職責(zé)-協(xié)作)卡片的設(shè)計(jì)方法就是用來(lái)做這個(gè)事情的,每個(gè)類按照下面的術(shù)語(yǔ)描述。
□ 類名。
□ 職責(zé):它應(yīng)該做什么?
□ 協(xié)作者:要完成工作它要與其他什么對(duì)象一起工作?
如何知道一個(gè)設(shè)計(jì)是好的設(shè)計(jì),或者正合適?代碼很自然地為設(shè)計(jì)的好壞提供了最好的反饋。如果需求有了小的變化,它仍然容易去實(shí)現(xiàn),那么它就是好的設(shè)計(jì)。而如果小的需求變化就帶來(lái)一大批基礎(chǔ)代碼的破壞,那么設(shè)計(jì)就需要改進(jìn)。
好設(shè)計(jì)是一張地圖,它也會(huì)進(jìn)化。設(shè)計(jì)指引你向正確的方向前進(jìn),它不是殖民地,它不應(yīng)該標(biāo)識(shí)具體的路線。你不要被設(shè)計(jì)(或者設(shè)計(jì)師)操縱。
切身感受好的設(shè)計(jì)應(yīng)該是正確的,而不是精確的。也就是說(shuō),它描述的一切必須是正確的,不應(yīng)該涉及不確定或者可能會(huì)發(fā)生變化的細(xì)節(jié)。它是目標(biāo),不是具體的處方。
平衡的藝術(shù)
n “不要在前期做大量的設(shè)計(jì)”并不是說(shuō)不要設(shè)計(jì)。只是說(shuō)在沒(méi)有經(jīng)過(guò)真正的代碼驗(yàn)證之前,不要陷入太多的設(shè)計(jì)任務(wù)。當(dāng)對(duì)設(shè)計(jì)一無(wú)所知的時(shí)候,投入編碼也是意見(jiàn)危險(xiǎn)的事。如果深入編碼只是為了學(xué)習(xí)或創(chuàng)造原型,只要你隨后能把這些代碼扔掉,那也是一個(gè)不錯(cuò)的辦法。
n 即使初始的設(shè)計(jì)到后面不再管用,你仍需設(shè)計(jì):設(shè)計(jì)行為是無(wú)價(jià)的。正如美國(guó)總統(tǒng)艾森豪威爾所說(shuō):“計(jì)劃是沒(méi)有價(jià)值的,但計(jì)劃的過(guò)程是必不可少的。”在設(shè)計(jì)過(guò)程中學(xué)習(xí)是價(jià)值的,但設(shè)計(jì)本身也許沒(méi)有太大的用處。
n 白板、草圖、便利貼都是非常好的設(shè)計(jì)工具。復(fù)雜的建模工具只會(huì)讓你分散精力,而不是啟發(fā)你的工作。
12.合理地使用技術(shù)
“你開(kāi)始了一個(gè)新的項(xiàng)目,在你面前有一個(gè)長(zhǎng)串關(guān)于新技術(shù)和應(yīng)用框架的列表。這些都是好東西,你真的需要使用列表中所有的技術(shù)。想一想,你的簡(jiǎn)歷上將留下漂亮的一筆,用那些偉大的框架,你的新應(yīng)用將具有極高技術(shù)含量。”
從前,作者之一Venkat的同事Lisa向
他解釋自己的提議:她打算使用EJB。
Venkat表示對(duì)EJB有些顧慮,覺(jué)得它
不適合那個(gè)特殊的項(xiàng)目。然后Lisa回答
道:“我已經(jīng)說(shuō)服了我們經(jīng)理,這是正確的技術(shù)路線,所以現(xiàn)在不要用扔‘炸彈’了。”這是一個(gè)典型的“簡(jiǎn)歷驅(qū)動(dòng)設(shè)計(jì)”的例子,之所以選擇這個(gè)技術(shù),是因?yàn)樗苊?#xff0c;也許還能提高程序員的技能。但是,盲目的為項(xiàng)目選擇技術(shù)框架,就好比是為了節(jié)省稅款而生孩子,這是沒(méi)有道理的。
在考慮引入新技術(shù)或框架之前,先要把你需要解決的問(wèn)題找出來(lái)。你的表達(dá)方式不同,會(huì)讓結(jié)果有很大差異。如果你說(shuō)“我們需要xyzzy技術(shù),是因?yàn)椤!!!?#xff0c;那么就不太靠譜。你應(yīng)該這樣說(shuō):“。。。太難了”或者是“。。。花的時(shí)間太長(zhǎng)了”,或者類似的句子。找到了需要解決的問(wèn)題,接下來(lái)就要考慮:
□ 這個(gè)技術(shù)框架真能解決這個(gè)問(wèn)題嗎?是的,或許這是顯而易見(jiàn)的。但是,這個(gè)技術(shù)真能解決你面臨的那個(gè)問(wèn)題嗎?或者,更尖銳一點(diǎn)說(shuō),你是如何評(píng)估這個(gè)技術(shù)的?是通過(guò)市場(chǎng)宣傳還是道聽(tīng)途說(shuō)?要確保它能解決你的問(wèn)題,并沒(méi)有如何的毒副作業(yè)。如果需要,先做一個(gè)小的原型。
□ 你將會(huì)被它拴住嗎?一些技術(shù)是賊船,一旦你使用了它,就會(huì)被它套牢,再也不能回頭了。它缺乏可取消性(查閱【HT00】),當(dāng)條件發(fā)生變化時(shí),這可能對(duì)項(xiàng)目有致命打擊。我們要考慮它是開(kāi)放技術(shù)還是專利技術(shù),如果是開(kāi)放的技術(shù),那又開(kāi)放到什么程度?
□ 維護(hù)成本是多少?會(huì)不會(huì)隨著時(shí)間的推移,它的維護(hù)成本會(huì)非常昂貴?畢竟,方案的花費(fèi)不應(yīng)該高于要解決的問(wèn)題,否則就是一次失敗的投資。我們聽(tīng)說(shuō),有個(gè)項(xiàng)目的合同是支持一個(gè)規(guī)則引擎,引擎一年的維護(hù)費(fèi)用是5萬(wàn)美元,但是這個(gè)數(shù)據(jù)庫(kù)只有30條規(guī)則。這也太貴了。
當(dāng)你在考察一個(gè)框架(或者任何技術(shù))的時(shí)候,或許會(huì)被它提供的各種功能吸引。接著,在驗(yàn)證是否使用這個(gè)框架的時(shí)候,你可能只會(huì)考慮已經(jīng)發(fā)現(xiàn)的另外一些功能。但是,你真的需要這些功能嗎?也許為了迎合你發(fā)現(xiàn)的功能,你正在為它們找問(wèn)題。這很想站在結(jié)賬處一時(shí)沖動(dòng)而買無(wú)用的小零碎(那也正是商場(chǎng)把那些小玩意兒放到那里的原因)。
不久前,Venkat遇到了一個(gè)項(xiàng)目。咨詢師Brad把一個(gè)專有框架賣給了這個(gè)項(xiàng)目的管理者。在Venkat看來(lái),這個(gè)框架本身也許還有點(diǎn)兒意思,但是它根本不適合這個(gè)項(xiàng)目。
盡管如此,管理者卻堅(jiān)決認(rèn)為他們要使用它。Venkat非常禮貌地停手不干了。他不想成為絆腳石,阻礙他們的工作進(jìn)度。一年之后項(xiàng)目還沒(méi)有完成——他們花了好幾個(gè)月的時(shí)間編寫(xiě)代碼來(lái)維護(hù)這個(gè)框架,為了適應(yīng)這個(gè)框架,他們還修改了自己的代碼。
Andy有過(guò)相似的經(jīng)歷:他的客戶想完全透明地利用開(kāi)源,他們擁有“新技術(shù)大雜燴”,其中的東西太多,以至于無(wú)法讓所有的部分協(xié)同工作。
如果你發(fā)現(xiàn)自己在做一些花哨的東西(比
如從頭創(chuàng)建自己的框架),那就醒醒吧,聞
聞煙味有多大,馬上該起火了。你的代碼
寫(xiě)得越少,需要維護(hù)的東西就越少。
例如,如果你想開(kāi)發(fā)自己的持久層框架,記住Ted Neward 的評(píng)論:對(duì)象—關(guān)系的映射就是計(jì)算機(jī)科學(xué)的越南戰(zhàn)場(chǎng)(Ted Neward 曾寫(xiě)過(guò)The Vietnam of Computer Science 著名文章,逐一探討了對(duì)象—關(guān)系映射的缺點(diǎn)。——編者注)
根據(jù)需要選擇技術(shù)。首先決定什么是你需要的,接著為這些具體的問(wèn)題評(píng)估使用技術(shù)。對(duì)任何要使用的技術(shù),多問(wèn)一些挑剔的問(wèn)題,并真實(shí)地作出回答。
切身感受新技術(shù)就應(yīng)該像是新的工具,可以幫助你更好地工作,它自己不應(yīng)該成為你的工作。
平衡的藝術(shù)
n 或許在項(xiàng)目中真正評(píng)估技術(shù)方案還為時(shí)太早。那就好。如果你在做系統(tǒng)原型并要演示給客戶看,或許一個(gè)簡(jiǎn)單的散列表就可以代替數(shù)據(jù)庫(kù)了。如果你還沒(méi)有足夠的經(jīng)驗(yàn),不要急于決定用什么技術(shù)。
n 每一門技術(shù)都會(huì)有優(yōu)點(diǎn)和缺點(diǎn),無(wú)論它是開(kāi)源的還是商業(yè)產(chǎn)品、框架、工具或者語(yǔ)言,一定要清楚它的利弊。
n 不要開(kāi)發(fā)那些你容易下載到的東西。雖然有時(shí)需要從最基礎(chǔ)開(kāi)發(fā)所有你需要的東西,但那是相當(dāng)危險(xiǎn)和昂貴的。
13.保持可以發(fā)布
“我們剛試用的時(shí)候發(fā)現(xiàn)一個(gè)問(wèn)題,你需要立即修復(fù)它。放下你手頭的工作,去修復(fù)那個(gè)剛發(fā)現(xiàn)的問(wèn)題,不需要經(jīng)過(guò)正規(guī)的程序。不用告訴其他任何人——趕快讓它工作就行了。”
這聽(tīng)起來(lái)似乎沒(méi)什么問(wèn)題。有一個(gè)關(guān)鍵修復(fù)的代碼必須要提交到代碼庫(kù)。這只是一件小事,而且又很緊急,所以你就答應(yīng)了。
修復(fù)工作成功地完成了。你提交了代碼,繼續(xù)回到以前那個(gè)高優(yōu)先級(jí)的任務(wù)中。然后一聲尖叫。太晚了,你發(fā)現(xiàn)同事提交的代碼和你的代碼發(fā)生了沖突,現(xiàn)在你使得每個(gè)人都無(wú)法使用系統(tǒng)了。這將會(huì)發(fā)費(fèi)很多精力(和時(shí)間)才能讓系統(tǒng)重新回到可發(fā)布的狀態(tài)。現(xiàn)在你有麻煩了。你必須告訴大家,你不能交付你承諾的修復(fù)代碼了。而魔鬼在嘲笑:“哈哈哈!”
這時(shí)候,你的處境會(huì)很糟糕:系統(tǒng)無(wú)法發(fā)布了。你弄壞了系統(tǒng),或許會(huì)帶來(lái)更糟糕的后果。
1836年,當(dāng)時(shí)的墨西哥總統(tǒng)安東尼奧·洛佩斯·德·圣安那將軍,率領(lǐng)部隊(duì)穿越得克薩斯州西部,追趕敗退的薩姆·休斯頓將軍。當(dāng)圣安那的部隊(duì)到達(dá)得克薩斯州東南方向的布法羅河岸的沼澤地帶的時(shí)候,他命令自己的部隊(duì)就地休息。傳說(shuō)中認(rèn)為他是太過(guò)自信,甚至沒(méi)有安排哨兵。就在那個(gè)傍晚,休斯頓發(fā)動(dòng)了突然襲擊,這時(shí)圣安那的部隊(duì)已經(jīng)來(lái)不及編隊(duì)了。他們潰不成軍,輸?shù)袅诉@場(chǎng)決定性的戰(zhàn)爭(zhēng),從此永遠(yuǎn)改變了得克薩斯州的歷史(http://www.sanjacinto-museum.org/The_Battle/April_21st_1836.)。
任何時(shí)候只要你沒(méi)有準(zhǔn)備好,那就是敵人進(jìn)
攻你的最佳時(shí)機(jī)。好好想一想,你的項(xiàng)目進(jìn)
入不可發(fā)布狀態(tài)的頻率是多少?你的源代碼
服務(wù)器中的代碼,是不是像圣安那在那個(gè)決定性的黃昏——沒(méi)有進(jìn)行編隊(duì),遇到緊急情況無(wú)法立即啟動(dòng)。
在團(tuán)隊(duì)里工作,修改一些東西的時(shí)候必須很謹(jǐn)慎。你要時(shí)刻警惕,每次改動(dòng)都會(huì)影響系統(tǒng)的狀態(tài)和整個(gè)團(tuán)隊(duì)的工作效率。在辦公室的廚房里,你不能容忍任何人亂丟垃圾,為什么就可以容忍一些人給項(xiàng)目帶來(lái)垃圾代碼呢?
下面是一個(gè)簡(jiǎn)單的工作流程,可以防止你提交破壞系統(tǒng)的代碼。
□ 在本地運(yùn)行測(cè)試。先保證你完成的代碼可以編譯,并且能通過(guò)所有的單元測(cè)試。接著確保系統(tǒng)中的其他測(cè)試都可以通過(guò)。
□ 檢出最新的代碼。從版本控制系統(tǒng)中更新代碼到最新的版本,再編譯和運(yùn)行測(cè)試。這樣往往會(huì)發(fā)現(xiàn)讓你吃驚的事情:其他人提交的新代碼和你的代碼發(fā)生了沖突。
□ 提交代碼。現(xiàn)在是最新的代碼了,并且通過(guò)了編譯和測(cè)試,你可以提交它們了。
在做上面事情的時(shí)候,也許你會(huì)遇到這樣一個(gè)問(wèn)題——其他人提交了一些代碼,但是沒(méi)有通過(guò)編譯或者測(cè)試。如果發(fā)生了這樣的事情,要立即讓他們知道,如果有需要,可以同時(shí)警告其他的同事。當(dāng)然,最好的辦法是,你有一個(gè)持續(xù)集成系統(tǒng),可以自動(dòng)集成并報(bào)告集成結(jié)果。
這聽(tīng)起來(lái)似乎有點(diǎn)恐怖,其實(shí)很簡(jiǎn)單。持續(xù)集成系統(tǒng)就是在后臺(tái)不停的檢出、構(gòu)建和測(cè)試代碼的應(yīng)用。你可以自己使用腳本快速實(shí)現(xiàn)這樣的方式,但如果你選擇已有的免費(fèi)、開(kāi)源的解決方案,它們會(huì)提供更多的功能且更加溫低能。有興趣的話,可以看一看Martin Fowler的文章(http://www.martinfowler.com/articles/continuousIntegration.html),或者是Mike Clark編著的圖書(shū)《項(xiàng)目自動(dòng)化之道》【Cla04】。
再深入一點(diǎn),假設(shè)你得知即將進(jìn)行的一次重大修改很可能會(huì)破壞系統(tǒng),不要人氣發(fā)生,應(yīng)該認(rèn)真地警告大家,在代碼提交之前,找出可以避免破壞系統(tǒng)的方法。選擇可以幫助你平滑地引入和轉(zhuǎn)換這些修改的方法,從而在開(kāi)發(fā)過(guò)程中,系統(tǒng)可以得到持續(xù)的測(cè)試和反饋。
雖然保持系統(tǒng)可以發(fā)布非常重要,但不會(huì)總是那么容易,例如,修改了數(shù)據(jù)庫(kù)的表結(jié)構(gòu)、外部文件的格式,或者休息的格式。這些修改,通常會(huì)影響應(yīng)用的大部分代碼,甚至導(dǎo)致應(yīng)用暫時(shí)不可用,直到大量的代碼修改完。盡管如此,你還是有辦法減輕這樣的痛苦。
為數(shù)據(jù)庫(kù)的表結(jié)構(gòu)、外部文件,甚至引用它的API提供版本支持,這樣所有相關(guān)變化都可以進(jìn)行測(cè)試。有了版本功能,所作的變化可以與其他代碼基相隔離,所以應(yīng)用的其他方面仍然可以繼續(xù)開(kāi)發(fā)和測(cè)試。
你也可以在版本控制系統(tǒng)中添加一個(gè)分支,專門處理這個(gè)問(wèn)題(使用分之需要十分小心,不好的分值也許會(huì)給你帶來(lái)更多的麻煩。詳情可以查閱《版本控制之道——CVS》或《版本控制之道——Subversion》)。保持你的項(xiàng)目時(shí)刻可以發(fā)布。保證你的系統(tǒng)隨時(shí)可以編譯、運(yùn)行、測(cè)試并立即部署。
切身感受你會(huì)覺(jué)得,不管什么時(shí)候,你的老板、董事長(zhǎng)、質(zhì)量保障人員、客戶或者你的配偶來(lái)公司參觀項(xiàng)目的時(shí)候,你都能很自信并毫不猶豫地給他們演示最新構(gòu)建的軟件。你的項(xiàng)目一直處于可以運(yùn)行的狀態(tài)。
平衡的藝術(shù)
□ 有時(shí)候,做一些大地改動(dòng)后,你無(wú)法花費(fèi)太多的時(shí)間和精力去保證系統(tǒng)一直可以發(fā)布。如果總共需要一個(gè)月的時(shí)間才能保證它一周內(nèi)可以發(fā)布,那就算了。但這只應(yīng)該是例外,不能養(yǎng)成習(xí)慣。
□ 如果你不得不讓系統(tǒng)長(zhǎng)期不可以發(fā)布,那就做一個(gè)(代碼和框架的)分支版本,你可以繼續(xù)進(jìn)行自己的實(shí)驗(yàn),如果不行,還可以撤銷,從頭再來(lái)。千萬(wàn)不能讓系統(tǒng)既不可以發(fā)布,又不可以撤銷。
14.提早集成,頻繁集成.
“只要沒(méi)有到開(kāi)發(fā)的末尾階段,就不要過(guò)早的浪費(fèi)時(shí)間去想如何集成你的代碼.至少也要等到開(kāi)發(fā)差不多的時(shí)候,才開(kāi)始考慮它,畢竟還沒(méi)有完成開(kāi)發(fā),為什么要操心集成的事呢?在項(xiàng)目的末尾,你有充裕的時(shí)間來(lái)集成代碼.”我們說(shuō)過(guò),敏捷的一個(gè)主要特點(diǎn)就是持續(xù)開(kāi)發(fā),而不是三天打魚(yú)兩天曬網(wǎng)似的工作,特別是在幾個(gè)人一起開(kāi)發(fā)同一個(gè)功能的時(shí)候,更應(yīng)該頻繁地集成代碼.很多開(kāi)發(fā)者用一些美麗的借口,推測(cè)集成的時(shí)間.有時(shí)不過(guò)是為了多寫(xiě)一些代碼,或者是另一個(gè)子系統(tǒng)還有很多的工作要完成.他們很容易就會(huì)這樣想:”現(xiàn)在手頭上的工作壓力夠大了,到最后我才能做更多的工作.才能考慮到其他代碼.”經(jīng)常會(huì)聽(tīng)到這樣的借口:”我沒(méi)有時(shí)間進(jìn)行集成”或者”我在機(jī)器上設(shè)置集成環(huán)境太費(fèi)事了,我現(xiàn)在不想做它.”但是,在產(chǎn)品開(kāi)發(fā)過(guò)程中,集成是一個(gè)主要的風(fēng)險(xiǎn)區(qū)域,讓你的字系統(tǒng)不停的增長(zhǎng),不去做系統(tǒng)集成,就等于把自己置于越來(lái)越大的風(fēng)險(xiǎn)中.世界沒(méi)有你仍然會(huì)轉(zhuǎn)動(dòng).潛在的分歧會(huì)繼續(xù)增加.相反,盡可能早的集成也更容易發(fā)現(xiàn)風(fēng)險(xiǎn).這樣風(fēng)險(xiǎn)及相關(guān)的代價(jià)就會(huì)相當(dāng)?shù)?而等的時(shí)間越長(zhǎng).你也就會(huì)越痛苦.作者之一 Venkat小時(shí)候生活在印度欽奈市(Chennai),經(jīng)常趕火車去學(xué)校.想其它的大城市一樣.印度的交通非常擁擠.他每次必須在車還沒(méi)有停穩(wěn)的時(shí)候.就跳上去或者跳下來(lái).但,你不能從站的地方一下子跳上運(yùn)行的火車.我們?cè)谖锢碚n上學(xué)習(xí)過(guò)這種運(yùn)動(dòng)規(guī)律.而應(yīng)該是,首先你要沿著火車行駛的方向跑.邊跑邊抓住火車上的扶手.然后跳入火車中.軟件集成就想這一樣.如果你不斷地獨(dú)立開(kāi)發(fā).忽然有一天跳到集成這一步,千萬(wàn)不要為受到打擊而吃驚.也許你自己在項(xiàng)目中就有這樣的體會(huì):每次到項(xiàng)目結(jié)束的時(shí)候都覺(jué)得非常不爽.大家需要日日夜夜地進(jìn)行集成.當(dāng)你在公司昏天黑地地加班時(shí),唯一的好處就是可以享受到免費(fèi)的披薩.
獨(dú)立開(kāi)發(fā)和早期集成之間是具有張力的.當(dāng)你獨(dú)立開(kāi)發(fā)時(shí),會(huì)發(fā)現(xiàn)開(kāi)發(fā)速度更快,生產(chǎn)率更高.你可以更有效地解決出現(xiàn)的問(wèn)題.但那并不是意味著要你避免或延遲集成(見(jiàn)本頁(yè)側(cè)邊欄).你一般需要每天集成幾次,最好不要2~3天才集成一次.
當(dāng)早期就進(jìn)行集成的時(shí)候.你會(huì)看到子系統(tǒng)之間的
互交和影響,你就可以估算它們之間通信和共享的信息
數(shù)據(jù).你越早弄清楚這些問(wèn)題.越早解決它們.工作量就越
小.就好比是,剛開(kāi)始有3個(gè)開(kāi)發(fā)者,開(kāi)發(fā)著5萬(wàn)行的代碼.
后來(lái)是5000個(gè)開(kāi)發(fā)者進(jìn)行3000萬(wàn)行代碼的開(kāi)發(fā).相反.如果你推遲集成的時(shí)間.解決這些問(wèn)題就會(huì)變得很難.需要大量和大范圍地修改代碼.會(huì)造成項(xiàng)目延期和一片混亂.
提早集成.頻繁集成.代碼集成是主要的風(fēng)險(xiǎn)來(lái)源.要想規(guī)避這個(gè)風(fēng)險(xiǎn).只有提早集成.持續(xù)而有規(guī)律地進(jìn)行集成.切身感受如果你真正做對(duì)了.集成就不再會(huì)是一個(gè)繁重的任務(wù).它只是編寫(xiě)代碼周期中的一部分.集成時(shí)產(chǎn)生的問(wèn)題.都會(huì)是小問(wèn)題并且容易解決.
平衡的藝術(shù)
□ 成功的集成就意味著所以的單元測(cè)試不停地集成.正如醫(yī)學(xué)院界西波克拉底的誓言:首先,不要造成傷害.
□ 通常,每天要和同隊(duì)其他成員以前集成代碼好幾次,比如平均每天5~10次,甚至更多.但如果你每次修改一行代碼就集成一次,那效用肯定會(huì)縮水.如果你發(fā)現(xiàn)自己的大部分時(shí)間都在集成,而不是寫(xiě)代碼,那你一定是集成得過(guò)于頻繁.
□ 如果你集成得不夠頻繁(比如,你一天集成一次,一周一次,甚至更糟),也許就會(huì)發(fā)現(xiàn)整天在解決代碼集成帶來(lái)的問(wèn)題.而不是在專心寫(xiě)代碼.如果你集成的問(wèn)題很大.那一定是做得不夠頻繁.
□ 對(duì)那些原型和實(shí)驗(yàn)代碼,也許你想要獨(dú)立開(kāi)發(fā),而不要想在集成上浪費(fèi)時(shí)間.但是不能獨(dú)立開(kāi)發(fā)太長(zhǎng)的時(shí)間.一旦你有了經(jīng)驗(yàn).就要快速地開(kāi)始集成.
15.提早實(shí)現(xiàn)自動(dòng)化部署
“沒(méi)問(wèn)題,可以手工安裝產(chǎn)品,尤其是給質(zhì)量保證人員安裝,而且你不需要經(jīng)常自己動(dòng)手,他們都很擅長(zhǎng)復(fù)制需要的所有文件。”
系統(tǒng)能在你的機(jī)器上運(yùn)行,或者能在開(kāi)發(fā)測(cè)試者和測(cè)試人員的機(jī)器上運(yùn)行,當(dāng)讓效果很好,當(dāng)然很好。但是它同時(shí)也需要能夠部署在用戶的機(jī)器上。如果系統(tǒng)能運(yùn)行在開(kāi)發(fā)服務(wù)器上,那很好,但是它同時(shí)也要運(yùn)行在生產(chǎn)環(huán)境中。
這就意味著,你要能用一種可重復(fù)和可靠的方式,在目標(biāo)機(jī)器上部署你的應(yīng)用。不幸的是,大部分開(kāi)發(fā)者智慧在項(xiàng)目的尾期才開(kāi)始考慮部署問(wèn)題。結(jié)果經(jīng)常出現(xiàn)部署失敗,要么是少了依賴的組建,要么是少了一些圖片,要么就是目錄結(jié)構(gòu)有誤。
如果開(kāi)發(fā)者改變了應(yīng)用的目錄結(jié)構(gòu),或者是在不同的應(yīng)用之間創(chuàng)建和共享圖片目錄,很可能會(huì)導(dǎo)致安裝過(guò)程失敗。當(dāng)這些變化在人們印象中還很深的時(shí)候,你可以快速的找到各種問(wèn)題。但是幾周或者幾個(gè)月之后查找它們,特別是在給客戶演示的時(shí)候,可就不是一件鬧這好玩的事情了。
如果現(xiàn)在你還是手工幫助質(zhì)量保證人員安裝應(yīng)用,
花一些時(shí)間,考慮如何將安裝過(guò)程自動(dòng)化。這樣,
只要用戶需要,你就可以隨時(shí)為他們安裝系統(tǒng)。
要提早實(shí)現(xiàn)它,這樣質(zhì)量保證團(tuán)隊(duì)即可以測(cè)試應(yīng)
用,又可以測(cè)試安裝過(guò)程。如果還可以手工安裝應(yīng)用,那么最后把應(yīng)用部署到生產(chǎn)環(huán)境時(shí)會(huì)發(fā)生什么尼?就算公司給你加班費(fèi),你也不愿意為不同用戶的機(jī)器或不同地點(diǎn)的服務(wù)器上一遍又一遍的安裝應(yīng)用。
有了自動(dòng)化部署系統(tǒng)后,在項(xiàng)目開(kāi)發(fā)的整個(gè)過(guò)程中,會(huì)更容易適應(yīng)互相依賴的變化。很可能你在安裝系統(tǒng)的時(shí)候,會(huì)忘記添加需要的庫(kù)或組建------在任意一臺(tái)機(jī)器上運(yùn)行自動(dòng)化安裝程序,你很快就會(huì)知道什么丟失了。如果因?yàn)闉槿鄙倭艘恍┙M建或者庫(kù)不兼容而導(dǎo)致安裝失敗,這些問(wèn)題會(huì)很快浮現(xiàn)出來(lái)。
一開(kāi)始就實(shí)現(xiàn)自動(dòng)化部署應(yīng)用。使用部署系統(tǒng)安裝你的應(yīng)用,在不同的機(jī)器上用不同的配置文件測(cè)試依賴的問(wèn)題。質(zhì)量保證人員要像測(cè)試應(yīng)用一樣測(cè)試部署。
切身感受
這些工作都應(yīng)該是無(wú)形的。系統(tǒng)的安裝或者部署應(yīng)該簡(jiǎn)單,可靠及可重復(fù)。一切都很自然。
平衡的藝術(shù)
¨ 一般產(chǎn)品在安裝的時(shí)候,都需要有相應(yīng)的軟,硬件的環(huán)境。比如,Java或Ruby的某個(gè)版本,外部數(shù)據(jù)庫(kù)或者操作系統(tǒng)。這些環(huán)境的不同很可能會(huì)導(dǎo)致很多技術(shù)支持的電話。所以檢查這些依賴關(guān)系,也是安裝過(guò)程的一部分。
¨ 在沒(méi)有詢問(wèn)并征的用戶的同意之前,安裝程序絕對(duì)不能刪除用戶的數(shù)據(jù)。
¨ 部署一個(gè)緊急修復(fù)的bug應(yīng)該很簡(jiǎn)單,特別是在生產(chǎn)服務(wù)器的環(huán)境中。你知道這會(huì)發(fā)生,而且你不想在壓力之下,在凌晨3點(diǎn)半,你還在手工部署系統(tǒng)。
¨ 用戶應(yīng)該可以安全并且完整的卸載安裝程序,特別是在質(zhì)量保證人員的機(jī)器環(huán)境中。
¨ 如果維護(hù)安裝腳本變得很困難,那很可能是一個(gè)早期的警告,預(yù)示著----很高的維護(hù)成本(或者不好的設(shè)計(jì)決策)
¨ 如果你打算把持續(xù)部署系統(tǒng)和產(chǎn)品CD或者DVD刻錄機(jī)連接到一起,你就可以自動(dòng)的為每個(gè)構(gòu)建制作出一個(gè)完整且有標(biāo)簽的光盤。任何人想要最新的構(gòu)建,只要從架子上拿最上面的一張光盤安裝即可。
15.使用演示獲得頻繁反饋
“這不是你的過(guò)錯(cuò),問(wèn)題出在我們的客戶——那些麻煩的最終客戶和用戶身上。他們不停地更改需求,導(dǎo)致我們嚴(yán)重地延期。他們一次就應(yīng)該想清楚所有想要的東西,然后把這些需求給我們,這樣我們才能開(kāi)發(fā)出令他們滿意的系統(tǒng)。這才是正確的工作方式。”
你時(shí)常會(huì)聽(tīng)到一些人想要凍結(jié)需求。但 需求就像是流動(dòng)著的油墨
是,現(xiàn)實(shí)世界中的需求就像是流動(dòng)著的
油墨。你無(wú)法凍結(jié)需求,正如你無(wú)法 Requirements are as fluid as ink
凍結(jié)市場(chǎng)、競(jìng)爭(zhēng)、知識(shí)、進(jìn)化或者成長(zhǎng)一樣。就算你真的凍結(jié)了,也很可能是凍結(jié)了錯(cuò)的東西。如果你期望用戶在項(xiàng)目開(kāi)始之前,就能給你可靠和明確的需求,那就大錯(cuò)特錯(cuò)了,趕快醒醒吧!
沒(méi)有人的思想和觀點(diǎn)可以及時(shí)凍結(jié),特別是項(xiàng)目的客戶。就算是他們已經(jīng)告訴你想要的東西了,他們的期望和想法還是在不停地進(jìn)化——特別是他們?cè)谑褂眯孪到y(tǒng)的部分功能時(shí),他們才開(kāi)始意識(shí)到它的影響和可能發(fā)生的問(wèn)題。這就是人的本性。
作為人類,不管是什么事情,我們都能越做越好,不過(guò)是以緩慢而逐步的方式。你的客戶也一樣。在給了你需求之后,他們會(huì)不停地研究這些功能,如何才能讓它們變得更好使用。如果,你覺(jué)得自己要做的所有工作就是按照用戶最初的需求,并實(shí)現(xiàn)了它們,但是在交付的時(shí)候,需求已經(jīng)發(fā)生了變化,你的軟件可能不會(huì)令他們滿意。在軟件開(kāi)發(fā)過(guò)程中,你將自己置于最大的風(fēng)險(xiǎn)中:你生產(chǎn)出了他們?cè)?jīng)要求過(guò)的軟件,但卻不是他們現(xiàn)在真正想要的。那最后的結(jié)果就是:驚訝、震驚和失望,而不是滿意。
幾年前的一次數(shù)值分析課上,老師要求Venkat使用一些偏微分方程式模擬宇宙飛船的運(yùn)行軌線。
程序基于時(shí)間t的坐標(biāo)點(diǎn),計(jì)算出在時(shí)間t+8的位置。程序最后繪出來(lái)的軌線圖就是如圖4-1中的虛線。暫無(wú)圖片
我們發(fā)現(xiàn),估算出來(lái)的宇宙飛船位置遠(yuǎn)遠(yuǎn)地偏離了它的真實(shí)位置。萬(wàn)有引力不是只在我們計(jì)算的坐標(biāo)點(diǎn)上才起作用。實(shí)際上,萬(wàn)有引力一直起作用:它是連接的,而不是離散的。由于忽略了點(diǎn)之間的作用力,我們的計(jì)算不斷引入了誤差,所以宇宙飛船最后到達(dá)了錯(cuò)誤的地方。
縮小點(diǎn)之間的間隔(就是δ的值),再運(yùn)行計(jì)算程序,誤差就會(huì)減少。這時(shí),估算的位置(如圖4-1中的實(shí)線)就是和實(shí)際位置很接近了。
同理,你的客戶的期望就像宇宙飛船的實(shí)際位置。軟件開(kāi)發(fā)的成功就在于最后你離客戶的期望有多近。你計(jì)算的每個(gè)精確位置,就是一個(gè)給客戶演示目前已經(jīng)完成功能的機(jī)會(huì),也正是得到用戶反饋的時(shí)候。在你動(dòng)身進(jìn)入下一段旅程的時(shí)候,這些反饋可以用來(lái)糾正你的方向。
我們經(jīng)常看到,給客戶演示所完成功能的時(shí)間與得到客戶需求的時(shí)間間隔越長(zhǎng),那么你就會(huì)離最初需求越來(lái)越遠(yuǎn)。
應(yīng)該定期地,每隔一段時(shí)間,例如一個(gè)迭代的結(jié)束,就與客戶會(huì)晤,并且演示你已經(jīng)完成的功能特性。
如果你能與客戶頻繁協(xié)商,根據(jù)他們的反饋開(kāi)發(fā),每個(gè)人都可以從中受益。客戶會(huì)清楚你的工作進(jìn)度。反過(guò)來(lái),他們也會(huì)提煉需求,然后趁熱反饋到你的團(tuán)隊(duì)中。這樣,他們就會(huì)基于自己進(jìn)化的期望和理解為你導(dǎo)航,你編寫(xiě)的程序也就越來(lái)越接近他們的真實(shí)需求。客戶也會(huì)基于可用的預(yù)算和時(shí)間,根據(jù)你們真實(shí)的工作進(jìn)度,排列任務(wù)的優(yōu)先級(jí)。
較短的迭代周期,會(huì)對(duì)頻繁的反饋有負(fù)面影響嗎?在宇宙飛船軌線的程序中,當(dāng)δ降低的時(shí)候,程序運(yùn)行就要發(fā)費(fèi)更長(zhǎng)的時(shí)間。也許你會(huì)覺(jué)得,使用短的迭代周期會(huì)使工作變慢,延遲項(xiàng)目的交付。
讓我們從這個(gè)角度思考:兩年來(lái)一直拼命地開(kāi)發(fā)項(xiàng)目,直到快結(jié)束的時(shí)候,你和你的客戶才發(fā)現(xiàn)一個(gè)基礎(chǔ)功能有問(wèn)題,而且它是一個(gè)核心的需求。你以為缺貨訂單是這樣處理的,但這完全不是客戶所想的東西。現(xiàn)在,兩年之后,你完成了這個(gè)系統(tǒng),寫(xiě)下了數(shù)百萬(wàn)行的代碼,卻背離了客戶的期望。再怎么說(shuō),兩年來(lái)辛苦寫(xiě)出的代碼有相當(dāng)大部分要重寫(xiě),代價(jià)是沉重的。
相反,如果你一邊開(kāi)發(fā),一邊向他們演示剛完成的功能。項(xiàng)目進(jìn)展了兩個(gè)月的時(shí)候,他們說(shuō):“等一下,缺貨訂單根本不是這么一回事。”于是,召開(kāi)一個(gè)緊急會(huì)議:你重新審查需求,評(píng)估要做多大的改動(dòng)。這時(shí)只要付很少的代價(jià),就可以避免災(zāi)難了。
要頻繁地獲得反饋。如果你的迭代周期是一個(gè)季節(jié)或者一年(那就太長(zhǎng)了),就應(yīng)把周期縮短到一周或者兩周。完成了一些功能和特征之后,去積極獲得客戶的反饋。
清晰可見(jiàn)的開(kāi)發(fā)。在開(kāi)發(fā)的時(shí)候,要保持應(yīng)有可見(jiàn)(而且客戶心中也要了解)。每隔一周或者兩周,邀請(qǐng)所有的客戶,給他們演示最新完成的功能,積極獲得他們的反饋。
切身感受項(xiàng)目啟動(dòng)了一段時(shí)間之后,你應(yīng)該進(jìn)入一種舒適的狀態(tài),團(tuán)隊(duì)和客戶建立了一種健康的富有創(chuàng)造性的關(guān)系。
突發(fā)事件應(yīng)極少發(fā)生。客戶應(yīng)該能感受到,他們可以在一定程度上控制項(xiàng)目的方向。
平衡的藝術(shù)
1 當(dāng)你第一次試圖用這種方法和客戶一起工作的時(shí)候,也許他們被這么多的發(fā)布嚇到了。所以,要讓他們知道,這些都是內(nèi)部的發(fā)布(演示),是為了他們自己的利益,不需要發(fā)布給全部的最終用戶。
2 一些客戶,也許會(huì)覺(jué)得沒(méi)有時(shí)間應(yīng)付每天、每周甚至一個(gè)月一次會(huì)議,那么就定一個(gè)月。
3 一些客戶的聯(lián)絡(luò)人的全職工作就是參加演示會(huì)議。他們巴不得每隔1個(gè)小時(shí)就有一次演示和反饋。你會(huì)發(fā)現(xiàn)這么頻繁的會(huì)議很難應(yīng)付,而且還要開(kāi)發(fā)代碼讓他們看。縮減次數(shù),只有在你做完一些東西可以給他們演示的時(shí)候,大家才碰面。定性的時(shí)候,不應(yīng)該拿來(lái)演示,那只能讓人生氣。可以及早說(shuō)明期望的功能:讓客戶知道,他們看到的是一個(gè)正在開(kāi)發(fā)中的應(yīng)用,而不是一個(gè)最終已經(jīng)完成的產(chǎn)品。
17使用短迭代,增量發(fā)布
“我們?yōu)楹竺娴?年制定了漂亮的項(xiàng)目計(jì)劃,列出了所有的任務(wù)和可交付的時(shí)間表。只要我們那時(shí)候發(fā)布了產(chǎn)品,就可以占領(lǐng)市場(chǎng)。”
統(tǒng)一過(guò)程和敏捷方法都使用迭代和增量開(kāi)發(fā)。使用增量開(kāi)發(fā)一次開(kāi)發(fā)應(yīng)用功能的幾個(gè)小組。每一輪的開(kāi)發(fā)都是基于前一次的功能,增加為產(chǎn)品增值的新功能。這時(shí),你就可以發(fā)布或者演示產(chǎn)品。
迭代開(kāi)發(fā)是,在小且重復(fù)的周期里,你完成各種開(kāi)發(fā)任務(wù):分析、設(shè)計(jì)、實(shí)現(xiàn)、測(cè)試和獲得反饋,所以叫做迭代。
迭代的結(jié)束就標(biāo)記一個(gè)里程碑。這時(shí),產(chǎn)品也許可用,也許不可用。在迭代結(jié)束時(shí),新的功能全部完成,你就可以發(fā)布,讓用戶真正地使用,同時(shí)提供技術(shù)支持、培訓(xùn)和維護(hù)方面的資源。每次增加的新功能都會(huì)包含多次迭代。
根據(jù)Capers Jones的格言:“…..大型系
統(tǒng)的開(kāi)發(fā)是一件非常危險(xiǎn)的事情。”大 給我一份詳細(xì)的長(zhǎng)期計(jì)劃,我就
型系統(tǒng)更容易失敗。它們通常不遵守迭 會(huì)給你一個(gè)注定完蛋的項(xiàng)目
代和增量開(kāi)發(fā)的計(jì)劃,或者迭代時(shí)間太 Show me a detailed log-term plan,
長(zhǎng)(更多關(guān)于迭代和演進(jìn)開(kāi)發(fā)的討論, and I’ll show you a project that’s
以及和風(fēng)險(xiǎn)的關(guān)系、生產(chǎn)率和缺點(diǎn),可 doomed
以查閱Agile and Iterative Development:A Manager’s Guide[Lar04]一書(shū))。Larman指出,軟件開(kāi)發(fā)不是精細(xì)的制造業(yè),而是創(chuàng)新活動(dòng)。規(guī)劃幾年之后客戶才能真正使用的項(xiàng)目注定是行不通的。
對(duì)付大項(xiàng)目,最理想的辦法就是小步前進(jìn),這也是敏捷方法的 。大步跳躍大大地增加了風(fēng)險(xiǎn),小步前進(jìn)才可以幫助你很好地把握平衡。
在你周圍,可以看到很多迭代和增量開(kāi)發(fā)的例子。比如W3C(萬(wàn)維網(wǎng)聯(lián)盟)提出的XML規(guī)范DTD(Document Type Definitions,文檔類型定義),它用來(lái)定義XML文檔的詞匯和結(jié)構(gòu),作為原規(guī)范的部分發(fā)布。雖然DTD設(shè)計(jì)的時(shí)候就解決了很多問(wèn)題,但是在真正使用的時(shí)候,又顯現(xiàn)出很多問(wèn)題和限制。基于用戶的反饋對(duì)規(guī)范就有了更深一層的理解,這樣就誕生了更加高效的第二代解決方案,例如Schema。如果他們一開(kāi)始就試圖進(jìn)行一些完美的設(shè)計(jì),也許就看不到XML成為今天的主流了——我們通過(guò)提早發(fā)布獲得了灼見(jiàn)和經(jīng)驗(yàn)。
大部分用戶都是希望現(xiàn)在就有一個(gè)夠用的軟件,而不是在一年之后得到一個(gè)超級(jí)好的軟件(可以參見(jiàn)《程序員修煉之道——從小工作專家》“足夠好的軟件”一節(jié)[HT00])。確定使產(chǎn)品可用的核心功能,然后把它們放在生產(chǎn)環(huán)節(jié)中,越早交到用戶的手里越好。
根據(jù)產(chǎn)品的特性,發(fā)布新的功能需要幾周或者幾個(gè)月的時(shí)間。如果是打算一年或者兩年再交付,你就無(wú)法應(yīng)該重新評(píng)估和重新計(jì)劃。也許你要說(shuō),構(gòu)建復(fù)雜的系統(tǒng)需要發(fā)費(fèi)時(shí)間,你無(wú)法用增量的方式開(kāi)發(fā)一個(gè)大型的系統(tǒng)。如果這種情況成立,就不要生產(chǎn)大的系統(tǒng)。可以把它分解成一塊塊有用的小系統(tǒng)——再進(jìn)行增量開(kāi)發(fā)。即使是美國(guó)國(guó)家航空天局(NASA)也使用迭代和增量開(kāi)發(fā)方式開(kāi)發(fā)用于航天飛機(jī)的復(fù)雜軟件(參見(jiàn)Design,Development,Integration: Space Shuttle Primary Flight Software System [MR84]).
詢問(wèn)用戶,哪些是使用產(chǎn)品可用且不可缺少的核心功能。不要為所有可能需要的華麗功能而分心,不要沉迷于你的想象,去做那些華而不實(shí)的用戶界面。
有一堆的理由,值得你盡快把軟件交到用戶手中:只要交到用戶手里,你就有了收入,這樣就有更好的理由繼續(xù)為產(chǎn)品投資了。從用戶那里得到的反饋,會(huì)讓我們進(jìn)一步理解什么是用戶真正想要的,以及下一步該實(shí)現(xiàn)哪些功能。也許你會(huì)發(fā)現(xiàn),一些過(guò)去認(rèn)為重要的功能,現(xiàn)在已經(jīng)不再重要了——我們都知道市場(chǎng)的變化有多快。盡快發(fā)布你的應(yīng)用,遲了也許它就沒(méi)有用了。
使用短迭代和增量開(kāi)發(fā),可以讓開(kāi)發(fā)者更加注于自己的工作。如果別人告訴你有一年的時(shí)間來(lái)完成系統(tǒng),你會(huì)覺(jué)得時(shí)間很長(zhǎng)。如果目標(biāo)很遙遠(yuǎn),就很難讓自己去專注于它。在這個(gè)快節(jié)奏的社會(huì),我們都希望更快地得到結(jié)果,希望更快地見(jiàn)到有形的東西。這不一定是壞事,相反,她會(huì)是一件好事,只要把它轉(zhuǎn)化成生產(chǎn)率和正面的反饋。
圖4-2描述了敏捷項(xiàng)目主要的周期關(guān)系。根據(jù)項(xiàng)目的大小,理想的發(fā)布周期是幾周到幾個(gè)月。在每個(gè)增量開(kāi)發(fā)周期里,應(yīng)該使用短的迭代(不應(yīng)該超過(guò)兩周)。每個(gè)迭代都要有演示,選擇可能提供反饋的用戶,給他們每人一份最新的產(chǎn)品副本。
增量開(kāi)發(fā)。發(fā)布帶有最小卻可用功能塊的產(chǎn)品。每個(gè)增量開(kāi)發(fā)中,使用1 ~ 4周左右迭代周期。
切身感受短迭代讓人感覺(jué)非常專注且具效率。你能看到一個(gè)實(shí)際并且確切的目標(biāo)。嚴(yán)格的最終期限迫使你做出一些艱難的決策,沒(méi)有遺留下長(zhǎng)期懸而未決的問(wèn)題。
平衡的藝術(shù)
o 關(guān)于迭代時(shí)間長(zhǎng)短一直是一個(gè)有爭(zhēng)議的問(wèn)題。Andy增經(jīng)遇到這樣一位客戶:他們堅(jiān)持認(rèn)為迭代就是4周的時(shí)間,因?yàn)檫@是他們學(xué)到的。但他們的團(tuán)隊(duì)卻因?yàn)檫@樣的步伐而垂死掙扎,因?yàn)樗麄儫o(wú)法在開(kāi)發(fā)新的代碼的同時(shí)又要維護(hù)很多已經(jīng)完成了的代碼。解決方案是,在每4周的迭代中間安排一周的維護(hù)任務(wù)。沒(méi)有規(guī)定說(shuō)迭代必須要緊挨著下一個(gè)迭代。
o 如果每個(gè)迭代的時(shí)間都不夠用,要么是任務(wù)太大,要么是迭代的時(shí)間太長(zhǎng)了。用戶的需要、技術(shù)和我們對(duì)需求的理解,都會(huì)隨著時(shí)間的推移而變化,在項(xiàng)目發(fā)布的時(shí)候,需要清楚地反映出這些變化。如果你發(fā)現(xiàn)自己工作時(shí)還帶有過(guò)時(shí)的觀點(diǎn)和陳腐的想法,那么很可能你等待太長(zhǎng)時(shí)間做調(diào)整了。
o 增量的發(fā)布必須是可用的,并且能為用戶提供價(jià)值。你怎么知道用戶會(huì)覺(jué)得有價(jià)值呢?這當(dāng)然要去問(wèn)用戶。
18:固定的價(jià)格就意味著背叛承諾“對(duì)這個(gè) 項(xiàng)目,我們必須要有固定的報(bào)價(jià),雖然我們還不清楚項(xiàng)目的具體情況,但任要有一個(gè)標(biāo)價(jià),到星期一,我需要整個(gè)團(tuán)隊(duì)的評(píng)估,并且我們必須要再年末交付真?zhèn)€項(xiàng)目。”
固定價(jià)格的合同會(huì)是敏捷團(tuán)隊(duì)的一個(gè)難題,我們一直在談?wù)撊绾斡贸掷m(xù),迭代和增量的方式工作。但是現(xiàn)在卻有人跑過(guò)來(lái),想提早知道她會(huì)花費(fèi)多少時(shí)間及多少成本。
從客戶方來(lái)看,這完全是理所應(yīng)當(dāng)?shù)摹?蛻粲X(jué)得做軟件就好比是蓋一棟樓房,或者是鋪設(shè)一個(gè)停車場(chǎng),等等。為什么軟件不能像建筑業(yè)等其他傳統(tǒng)的行業(yè)一樣呢?
也許它真的與建筑有很多相似之處------真正的建筑行業(yè),但不是我們想象中的建筑業(yè)。根據(jù)英國(guó)1998年的一個(gè)研究,由于錯(cuò)誤而返工的成本大約占整個(gè)項(xiàng)目成本的30%。這不是因?yàn)榭蛻舻男枨笞兓?#xff0c;也不是物理定律的變化,而是一些簡(jiǎn)單錯(cuò)誤。比如,橫梁太短,窗戶洞太大,等等。這些都是簡(jiǎn)單并且為人熟悉的錯(cuò)誤。
軟件項(xiàng)目會(huì)遭遇各種各樣的小錯(cuò)誤,還要加上基礎(chǔ)需求的變化(不,我要的不是一個(gè)工棚,而是一棟摩天大樓),不同個(gè)體和團(tuán)隊(duì)的能力差別費(fèi)城巨大(20倍,甚至更多),當(dāng)然,還不停地會(huì)有新技術(shù)出現(xiàn)(從現(xiàn)在開(kāi)始,釘子就變成圓形的了)。
軟件項(xiàng)目天生就是變化無(wú)常的,不可 固 定 的 價(jià) 格 就 是 保 證 要 背 叛 承諾
重復(fù)。如果要提前給出一個(gè)該規(guī)定的 A fiaed price guarantees a broken
價(jià)格,就幾乎肯定不能遵守開(kāi)化上的承 promise
諾。那么我們有什么可行的辦法呢?
我們能做更精確的評(píng)估嗎?或者商量出另一中約定。根據(jù)自己的處境,選擇不同的戰(zhàn)略。如果你的客戶一定要你預(yù)想確定項(xiàng)目的報(bào)價(jià)(比如政府合約),那么可能你需要研究一些重型的評(píng)估技術(shù),比如COCOMO模型或者功能點(diǎn)分析法(Function Point analysis)。但他們不屬于敏捷方法的范疇,并且是它們也要付出代價(jià)。如果這個(gè)項(xiàng)目本質(zhì)上和另一個(gè)項(xiàng)目十分相似,并且是同一個(gè)團(tuán)隊(duì)開(kāi)化的,那么你就好辦了:為一個(gè)用戶開(kāi)化的簡(jiǎn)單網(wǎng)站,也下一個(gè)會(huì)非常相似。但是,很多項(xiàng)目并不像上面所說(shuō)的那么如意。大部分項(xiàng)目都是業(yè)務(wù)應(yīng)用,一個(gè)用戶和另一個(gè)用戶都由著巨大的差別。項(xiàng)目的發(fā)掘和創(chuàng)造需要很多配合工作,或許你可以提供稍有不同的安排,試試下面的方法。o 主動(dòng)提議先構(gòu)建系統(tǒng)最初的、小的和有用的部分(用建筑來(lái)打個(gè)比方,就是先做個(gè)車庫(kù))。挑選一系列小的功能,這樣完成第一次交付應(yīng)該不多于6-8周。向客戶解釋,這時(shí)候還不是要完成所有的功能,而是要足夠一次交付,并能讓用戶真正使用。
o 第一個(gè)迭代結(jié)束時(shí)客戶有連個(gè)選擇:可以選擇一系列新的功能,繼續(xù)進(jìn)入下一個(gè)迭代;或者可以取消合同,僅需支付第一個(gè)迭代的幾周費(fèi)用,他們要么吧現(xiàn)在的成果扔掉,要么找其他的團(tuán)隊(duì)來(lái)完成它。
o 如果他們選擇繼續(xù)前進(jìn)。那這時(shí)候,應(yīng)該就能很好的預(yù)測(cè)下一個(gè)迭代工作。在下一個(gè)迭代結(jié)束時(shí)候。用戶仍然有同樣的選擇機(jī)會(huì);要么現(xiàn)在停止,要么繼續(xù)下一個(gè)迭代。對(duì)客戶來(lái)說(shuō),這種方式的好處是項(xiàng)目不可能會(huì)死亡。他們可以很早的看到工作的進(jìn)度(或者不足之處)。他們總是可以控制項(xiàng)目,可以隨時(shí)停止項(xiàng)目,不需要繳納任何的違約金,他們可以控制線完成哪些功能,并能精確地知道需要花費(fèi)多少資金。總而言之,客戶會(huì)承擔(dān)更低的風(fēng)險(xiǎn)。而你所做的就是在進(jìn)行迭代和增量開(kāi)化。基于真實(shí)的評(píng)估。讓團(tuán)隊(duì)和客戶一起,真正地在當(dāng)前項(xiàng)目中工作,做具體實(shí)際的評(píng)估。由客戶控制他們要的功能和預(yù)算。
切身感受你的評(píng)估數(shù)據(jù)會(huì)在整個(gè)項(xiàng)目中發(fā)生變化------他們不固定的。但是,你會(huì)覺(jué)得自信心在不斷增加,你會(huì)越來(lái)越清楚每個(gè)迭代可以完成的工作。隨著時(shí)間的推移,你的評(píng)估能力會(huì)不斷的提高。
平衡的藝術(shù)
o 如果你對(duì)答案不滿意,那么看看你是否可以改變問(wèn)題。
o 如果你是在一個(gè)基于計(jì)劃的非敏捷環(huán)境中工作,那么要么考慮一個(gè)基于計(jì)劃且非敏捷的開(kāi)化方法,要么換一個(gè)不同的環(huán)境。
o 如果你在完成第一個(gè)迭代開(kāi)化之前,拒絕做任何評(píng)估,也許你會(huì)失去這個(gè)合同,讓位于哪些提供了評(píng)估的人,無(wú)論他們做了多么不切實(shí)際的承諾。
o 敏捷不是一味著“開(kāi)始編碼,我們最終會(huì)知道何時(shí)可以完成”。你仍然需要根據(jù)當(dāng)前的知識(shí)和猜想,做一個(gè)大致的評(píng)估,解釋如何才能到達(dá)這個(gè)目標(biāo),并給出誤差范圍。
o 如果你現(xiàn)在別無(wú)選擇,你不的不提供一個(gè)固定的價(jià)格,那么你需要學(xué)到真正好的評(píng)估技巧。
o 也許你會(huì)考慮在合同中確定每個(gè)迭代的固定價(jià)格,但迭代的數(shù)量是可以商量的,他可以根據(jù)當(dāng)前的工作狀況進(jìn)行調(diào)整 [又名工作條款說(shuō)明(Statement of Work)]。
第5章 敏捷反饋一步行動(dòng),勝過(guò)千萬(wàn)專家的意見(jiàn).------Bill Nye,The Science Guy科普節(jié)目主持人
在敏捷項(xiàng)目中,我們小步前進(jìn),不停地收集反饋,時(shí)刻矯正自己.但是,這些反饋都是從何而來(lái)呢?
在上一章中,我們討論了與用戶一起緊密工作-----從他們那里獲得反饋,并且采取的行動(dòng),我們主要討論如何從其他渠道獲得反饋.按照Bill Nye的觀點(diǎn),實(shí)踐是絕對(duì)必需的.我們遵循這一原則,確保你明確知道項(xiàng)目的正確狀態(tài),而不是主觀臆測(cè).
很多項(xiàng)目,都是因?yàn)槌绦虼a失控而陷入困境.修復(fù)bug導(dǎo)致了更多的bug,從而又導(dǎo)致了更多的bug修復(fù),成堆的測(cè)試卡片最后會(huì)把項(xiàng)目壓垮.這時(shí),我們需要的是經(jīng)常的監(jiān)督-----頻繁反饋以確保代碼不會(huì)變壞,如果不會(huì)更好,至少能像昨天一樣繼續(xù)工作.在第78頁(yè),介紹如何讓守護(hù)天使業(yè)監(jiān)督你的代碼.
但是,這也不能防止你設(shè)計(jì)的接口或API變得笨重和難用.這時(shí),你就要先用它再實(shí)現(xiàn)它(從第82頁(yè)開(kāi)始介紹)
當(dāng)然,從第87頁(yè)開(kāi)始,可以看到為什么不同環(huán)境,就有不同問(wèn)題.
現(xiàn)在,你擁有了設(shè)計(jì)良好的API和干凈的代碼,就可以看看結(jié)果是否符合用戶的期望了.你可以通過(guò)自動(dòng)驗(yàn)收測(cè)試來(lái)保證代碼是正確的,并且一直都是正確的.我們從第90頁(yè)開(kāi)始談?wù)撨@個(gè)話題
人人都想清楚了解項(xiàng)目的進(jìn)度狀況,但有很容易誤入歧途,要么是被一些難懂的指示器誤導(dǎo),要么就是錯(cuò)誤迷信華麗的甘特圖,PERT圖或者日歷工具.其實(shí),你想要的是能度量真實(shí)的進(jìn)度,我們會(huì)在第93頁(yè)介紹他.
盡管,我們經(jīng)驗(yàn)談?wù)摿嗽陂_(kāi)發(fā)的時(shí)候,與用戶一起工作并及時(shí)得到用戶的反饋,但是在其他的比如產(chǎn)品發(fā)布之后的很長(zhǎng)一段時(shí)間,你還是需要在傾聽(tīng)用戶的聲音,我們會(huì)在第96頁(yè)詳細(xì)解釋.
守護(hù)天使
“你不必為單元測(cè)試花費(fèi)那么多時(shí)間和精力.它只會(huì)拖延項(xiàng)目的進(jìn)度.好歹,你也是一個(gè)不錯(cuò)的程序員—單元測(cè)試只會(huì)浪費(fèi)時(shí)間,我們現(xiàn)在正處于關(guān)鍵時(shí)刻.”
代碼在快速地變化.每當(dāng)你手指敲擊一下鍵盤,代碼就會(huì)被改變.敏捷就是管理變化的,而且,代碼可能是變化最頻繁的東西.
為了應(yīng)對(duì)代碼的變化,你需要持續(xù)獲得代碼健康狀態(tài)
的反饋:它是在做你期望的事情嗎?最近一次修改有沒(méi)有無(wú) 編寫(xiě)能產(chǎn)生反饋的代碼
意中破壞了什么功能?這時(shí),你就帶上守護(hù)天使,確保所有功能 Coding feedback
都能正常工作.要做到這樣,就需要自動(dòng)化單元測(cè)試.
現(xiàn)在,一些開(kāi)發(fā)者會(huì)對(duì)單元測(cè)試有意見(jiàn):畢竟,有”測(cè)試”這個(gè)詞在里面,毫無(wú)疑問(wèn)這應(yīng)該是其他人做的工作.從現(xiàn)在開(kāi)始,忘掉”測(cè)試”這個(gè)詞.就把它看作是一個(gè)極好,編寫(xiě)能產(chǎn)生反饋的代碼的技術(shù).
先回顧一下,在過(guò)去大部分開(kāi)發(fā)者是如何工作的:你寫(xiě)了一小塊代碼,然后嵌入一些輸入語(yǔ)句,來(lái)看一些關(guān)鍵變量的值.你也許是在調(diào)試器中或者基于一些樁(stub)程序來(lái)運(yùn)行代碼.你手工查看所有的運(yùn)行效果,來(lái)修復(fù)發(fā)現(xiàn)的所有問(wèn)題,然后扔掉那些樁代碼,或者從調(diào)試器中退出,再去解決下一個(gè)問(wèn)題.
敏捷式的單元測(cè)試正是采取了相同,相似的過(guò)程,并且還讓其更上一層樓.不用扔掉樁程序,你把它保存下來(lái),還要讓其可以自動(dòng)化地持續(xù)運(yùn)行.你編寫(xiě)代碼來(lái)檢查具體值,而不是手工檢查那些感興趣的變量.
用代碼來(lái)測(cè)試變量的具體值(以及跟蹤運(yùn)行了多少個(gè)測(cè)試),已經(jīng)是非常普通的做飯.你可以選擇一個(gè)標(biāo)準(zhǔn)的測(cè)試框架,來(lái)幫助你完成簡(jiǎn)單的編寫(xiě)和組織測(cè)試的工作,如java的Junit,C#或.NET的Nunit,測(cè)試web service的HttpUnit,等等.實(shí)際上,對(duì)如何你可以想象到的環(huán)境和語(yǔ)言都有對(duì)應(yīng)的單元測(cè)試框架,其中的大部分都可以從來(lái)Http://xprogramming.com/software.htm上的列表中找到.
只要有單元測(cè)試,就要讓它們自動(dòng)運(yùn)行.也就是每次編譯或者構(gòu)建代碼的時(shí)候,就運(yùn)行一次測(cè)試.把單元測(cè)試的結(jié)果看作是和編譯器一樣—如果測(cè)試沒(méi)有通過(guò)(或者沒(méi)有測(cè)試),那就像編譯沒(méi)有通過(guò)一樣糟糕.
接下來(lái)就是在后臺(tái)架設(shè)一個(gè)構(gòu)建機(jī)器,不懂獲得最新版本的源代碼,然后編譯代碼,并運(yùn)行單元測(cè)試,如果有如何錯(cuò)誤它會(huì)讓你及時(shí)知道.
結(jié)合本地單元測(cè)試,運(yùn)行每個(gè)編譯,構(gòu)建機(jī)器不斷編譯和運(yùn)行單元測(cè)試,這樣你就用有了一個(gè)守護(hù)天使.如果出現(xiàn)了問(wèn)題,你會(huì)立刻知道,并且這是最容易修復(fù)(也是成本最低)的時(shí)候.
一旦單元測(cè)試到位,采用這樣的回歸測(cè)試,你就可以隨意重構(gòu)代碼.可以根據(jù)需要進(jìn)行試驗(yàn),重新設(shè)計(jì)或者重寫(xiě)代碼:單元測(cè)試會(huì)確保你不會(huì)意外地破壞如何功能.這會(huì)讓你心情舒暢,你不用每次寫(xiě)代碼的時(shí)候都如履薄冰.
單元測(cè)試是最受歡迎者的一中敏捷實(shí)踐,有很多圖書(shū)和其他資料可以幫你起步.如果你是一個(gè)新手,建議閱讀<<單元測(cè)試之道>>(有java[Ht03]和C#[Ht04]版本).如果要進(jìn)一步了解測(cè)試的一些竅門,可以看一下Junit Recipes[Rai04].
如果需要自動(dòng)化地連接單元測(cè)試(和其他一些有用的東西),可以閱讀<<項(xiàng)目自動(dòng)化之道>>[Cla04].盡管它只要是關(guān)于java的,但也有類似的可以用與.Net環(huán)境或者其他環(huán)境的工具.
如果你仍然在尋找開(kāi)始單元測(cè)試的理由,下面有很多.
n 單元測(cè)試能及時(shí)提供反饋.你的代碼會(huì)重復(fù)得到鍛煉.但若修改或者重寫(xiě)了代碼,測(cè)試用例就會(huì)檢查你是否破壞了已有的功能.你可以快速得到反饋,并很容易的修復(fù)他們.
n 單元測(cè)試讓你的代碼更加健壯.測(cè)試幫助你全面思考代碼的行為,幫你練習(xí)正面和反面以及異常情況.
n 單元測(cè)試是有用的設(shè)計(jì)工具.正如我們?cè)趯?shí)踐20中談?wù)摰降?單元測(cè)試有助于實(shí)現(xiàn)簡(jiǎn)單的,注重實(shí)效的設(shè)計(jì).
n 單元測(cè)試是讓你自信的后臺(tái).你測(cè)試代碼,了解它在各種不同條件下的行為.這會(huì)讓你在面對(duì)新的任務(wù),時(shí)間緊迫的巨大壓力之下,找到自信.
n 單元測(cè)試是解決問(wèn)題是的探測(cè)器. 單元測(cè)試就像是測(cè)試印制電路板的示波鏡.當(dāng)問(wèn)題出現(xiàn)的時(shí)候,你可以快速地給代碼發(fā)送一個(gè)脈沖信號(hào).這為你提供了一個(gè)很自然的發(fā)現(xiàn)和解決問(wèn)題的方法(見(jiàn)習(xí)慣35,第136頁(yè)).
n 單元測(cè)試是可信的文檔.當(dāng)你開(kāi)始學(xué)習(xí)新API的時(shí)候,它的單元測(cè)試是最精確的可靠的文檔.
n 單元測(cè)試是學(xué)習(xí)工具.在你開(kāi)始學(xué)習(xí)新API的時(shí)候,可以為這個(gè)API寫(xiě)個(gè)單元測(cè)試,從而加深自己的理解.這寫(xiě)學(xué)習(xí)用的測(cè)試,不僅能幫助你理解API的行為,還能保證你快速找到以后可能引入的,無(wú)法與現(xiàn)有代碼兼容的變化.
使用自動(dòng)化的單元測(cè)試.好的單元測(cè)試能夠?yàn)槟愕拇a問(wèn)題提供及時(shí)的警報(bào).如果沒(méi)有到位的單元測(cè)試,不要進(jìn)行任何設(shè)計(jì)和代碼修改.
切身感受
你依賴與單元測(cè)試.如果代碼沒(méi)有測(cè)試,你會(huì)覺(jué)得很不舒服,就像是在高空作業(yè)沒(méi)有系安全帶一樣.
平衡的藝術(shù)
n 單元測(cè)試是優(yōu)質(zhì)股,值得投資.但一些簡(jiǎn)單的屬性訪問(wèn)方法或者價(jià)值不大的方法,是不值得花費(fèi)時(shí)間進(jìn)行測(cè)試的.
n 人們不編寫(xiě)單元測(cè)試的很多借口都是因?yàn)榇a中的設(shè)計(jì)缺陷.通常,抗議越強(qiáng)烈,就說(shuō)明越糟糕.
n 單元測(cè)試只有在達(dá)到一定測(cè)試覆蓋率的時(shí)候,才能真正地發(fā)揮作用.你可以使用一些測(cè)試覆蓋率工具,大致了解自己的單元測(cè)試的翻蓋情況.
n 不測(cè)試越多質(zhì)量就會(huì)越高,測(cè)試必須要有效.如果測(cè)試無(wú)法發(fā)現(xiàn)任何問(wèn)題,也許它們就是沒(méi)有測(cè)試對(duì)路.
先用它在來(lái)實(shí)現(xiàn)它
“請(qǐng)進(jìn),先完成所有的代碼庫(kù),后面會(huì)有大量時(shí)間看到用戶是否是如何思考,現(xiàn)在只有把代碼仍過(guò)去就可以了,我保證它沒(méi)有問(wèn)題”
很多成功的公司都是靠著“吃自己的狗食”活著,也就是說(shuō),如果要讓你的產(chǎn)品盡可能的好,自己先要積極地使用它
幸運(yùn)的是,我們不是在狗食業(yè)務(wù),但是,我們的業(yè)務(wù)是要?jiǎng)?chuàng)造出能調(diào)用的API和可以使用的接口,事實(shí)上,在你剛做完設(shè)計(jì)但還沒(méi)完成后面的實(shí)現(xiàn)的時(shí)候,應(yīng)使用它,這個(gè)可行嗎?
使用 被稱為TDD(Test Driven Development,測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的技術(shù),你總是在一個(gè)失敗的單元測(cè)試后才開(kāi)始編碼,測(cè)試)失敗要么是因?yàn)?測(cè)試方法不存在,要么是因?yàn)榉椒ǖ倪壿嫴蛔阋宰? 編寫(xiě)能產(chǎn)生反饋的代碼
測(cè)試通過(guò)。
Writer test before writing code先測(cè)試,你就會(huì)站在代碼用戶的角度去思考,而不僅僅是一個(gè)單純的實(shí)現(xiàn)者,這樣就是很大區(qū)別,你就會(huì)發(fā)現(xiàn)以為自己要使用它們,所以能設(shè)計(jì)一個(gè)更有,更一致的接口
除止之外,先寫(xiě)測(cè)試有助于消除過(guò)度復(fù)雜的設(shè)計(jì),讓你可以會(huì)考慮需要這些類,例如:TicTacToeBoard,Cell,Row,Colum,Player,User,Peg,Score和Rueles咱們從TicTacToeBoard類開(kāi)始,它就代表了井字旗本身(從游戲的刻心邏輯而不是UI角度說(shuō))
這可能是TicTacToeBoard類的第一測(cè)試,是用C#在NUtil測(cè)試框架下編寫(xiě)的, 它創(chuàng)造了一個(gè)游戲面板,用斷言來(lái)檢查游戲沒(méi)有結(jié)束。
【TestFixture】
Public class TicTaToeTest
{
Private TicTaToeTest board;
[SetUp]
Public void createBord()
{
Board=new TicTacToeTest();
}
[Test]
Public void TestCreateBoard()
{
Assert.IsNotNull(board);
Assert.IsFalse(borad.Gameover);
}
}測(cè)試失敗,因?yàn)轭怲icTactoeBoard 還不存在,你會(huì)得到一個(gè)編譯錯(cuò)誤,如果它通過(guò)了,你一定很驚訝,不是嗎?這也可能會(huì)發(fā)生,只是概率很小,但確實(shí)可能發(fā)生,在測(cè)試通過(guò)之前,先要確保測(cè)試是失敗的,目的是希望暴露出測(cè)試中潛伏在的bug. 下面我們來(lái)實(shí)現(xiàn)這個(gè)類。Public class TicTacToeBoard{
Public bool gameOver{
Get{
Return false;
}
}
}
在屬性GameOver中,我們現(xiàn)在只返回false,一般情況下,你會(huì)用到必要色最少代碼讓測(cè)試通過(guò),從某種角度上說(shuō),這就是在欺騙測(cè)試——你知道代碼還沒(méi)有完成。但是沒(méi)有關(guān)系,后面的測(cè)試會(huì)迫使你在返回來(lái)的繼承添加新的功能。
下一步是什么呢?首先,你必須決定誰(shuí)先開(kāi)始走第一步旗,我們就要設(shè)第一個(gè)比賽者。先為第一個(gè)比賽者先一個(gè)測(cè)試。
[Test]
Public void TestSetPirstPlayer(){
//what should go here?
}
這時(shí),測(cè)試會(huì)迫使你做一個(gè)決定,在完成它之前,你必須決定如何在代碼中表示
比賽者,如何把它們分配到面板上。這里有一個(gè)主意。
Board。SetFirstPlayer(new Player(”Mark”),”x”);
這會(huì)告訴面板,游戲玩家Mark使用X。
這樣當(dāng)然可以。你真的需要Player這個(gè)類。或者第一個(gè)玩家的名字嗎?也許,稍后你需要知道誰(shuí)是贏家,但現(xiàn)在它還不是問(wèn)題。YANG(你可以永遠(yuǎn)都不需要它)原則說(shuō)過(guò),如果不是真正需要他的時(shí)候,你就不應(yīng)該實(shí)行這個(gè)功能。基于這一點(diǎn),現(xiàn)在還沒(méi)有足夠的理由表示你需要的理由表示你需要Player這個(gè)類。
別忘了,我們還沒(méi)有實(shí)行TicTacToeBoard類中的SetFirstPlayer()方法,并且
還沒(méi)有寫(xiě)play這個(gè)類,我們?nèi)匀皇菍?xiě)一個(gè)測(cè)試,我們假設(shè)下面的代碼是用來(lái)設(shè)置第一個(gè)玩家的Board。SetFirstPlayer(”x”);
它表示設(shè)X為第一玩家,比第一版本需要簡(jiǎn)單,但是,這個(gè)版本隱藏著風(fēng)險(xiǎn),你可以傳任何字母給SetFirestPlayer()方法,這就是意味著你必須添加代碼來(lái)檢查參數(shù)是O還是X,并且需要知道如果它不是這兩個(gè)值的時(shí)候該如何處理。因此要進(jìn)一步簡(jiǎn)單化,我們有一個(gè)簡(jiǎn)單的標(biāo)志來(lái)標(biāo)明第一個(gè)玩家是O還是X,知道了這個(gè),我們現(xiàn)在就可以寫(xiě)單元測(cè)試了
[Test]
Public void TestSetFirstPlay(){
Board.FirstPlayPegIsx=true;
Assert.IsTrue(board.FirstPlayerPegIsx);
}
我們可以將 FirstPlayerpegIsx 設(shè)為布爾型的屬性,并把它設(shè)為期望的值。你看起來(lái)挺簡(jiǎn)單,也容易使用,比復(fù)雜的Player類容易很多,測(cè)試寫(xiě)好了。你就可以通過(guò)TicTacToeBoard類中實(shí)現(xiàn)FirestPlayerPegIsx屬性,讓測(cè)試通過(guò)。
你看,我們是以paayer類開(kāi)始,最后卻只使用了簡(jiǎn)單的布爾型的屬性。這是如何做到的呢?這種簡(jiǎn)化就是編寫(xiě)代碼之前讓測(cè)試優(yōu)先實(shí)現(xiàn)的。
但記住,我們不是要扔掉好的設(shè)計(jì),就只用大量的布爾型來(lái)編碼所有的東西。這里重點(diǎn)是:什么是成功的實(shí)現(xiàn)待定功能的最低成本,總之,程序員很容易走向另一個(gè)極端—— 一些不必要的過(guò)于復(fù)雜的事情——測(cè)試優(yōu)先會(huì)幫助我們,防止我們走偏。
消除那些還沒(méi)有編寫(xiě)的類,這會(huì)很容易的簡(jiǎn)化代碼。相反,一旦你已經(jīng)編寫(xiě)了代碼,也許會(huì)強(qiáng)迫自己保留這些代碼,并繼續(xù)使用它|(即使代碼已經(jīng)過(guò)期作廢很久了)
移動(dòng)設(shè)計(jì)并不意味著需要更多的類
Good design cloesn’t me
當(dāng)你開(kāi)發(fā)設(shè)計(jì)面向?qū)ο蟮南到y(tǒng)的時(shí)候,
可能會(huì)迫使自己使用對(duì)象,有一種傾向
認(rèn)為,面向?qū)ο蟮南到y(tǒng)應(yīng)該有對(duì)象的系統(tǒng)
應(yīng)該對(duì)象組成,我們迫使自己創(chuàng)建越來(lái)越多
的對(duì)象類,不管他們是否真的需要,添加物
用代碼總是不好的想法TDD有機(jī)會(huì)讓你編寫(xiě)的代碼之前(或者至少深入到實(shí)現(xiàn)之前),可以深思熟慮將如何用它,這會(huì)迫使你去思考它的可用性和便利性,并然你的設(shè)計(jì)更加注重實(shí)效。
當(dāng)然,設(shè)計(jì)不是開(kāi)始編碼的時(shí)候就結(jié)束了,你需要在它色生命周期中持續(xù)地添加測(cè)試,添加代碼,并重新設(shè)計(jì)代碼(更多信息參考第133也習(xí)慣28)
先用它在來(lái)實(shí)現(xiàn)它。將TDD作為設(shè)計(jì)工具,它會(huì)為你帶來(lái)更簡(jiǎn)單游實(shí)數(shù)的設(shè)計(jì)
切身感受
這種感受就是,只在有具體理由的時(shí)候才開(kāi)始編碼,你可以專注設(shè)計(jì)接口。而不會(huì)被很多實(shí)現(xiàn)的細(xì)節(jié)干擾
平衡的藝術(shù)
n 不要把測(cè)試優(yōu)先和提交代碼之前的測(cè)試等同起來(lái),測(cè)試先行可以幫助你改進(jìn)設(shè)計(jì),但是你還是需要在提交代碼之前做的測(cè)試。
n 任何一個(gè)設(shè)計(jì)都可以被改進(jìn)
n 你在驗(yàn)證一個(gè)想法或者設(shè)計(jì)一個(gè)原型的時(shí)候,單元測(cè)試也許不適合,但是。萬(wàn)一這些代碼不幸倉(cāng)促演變成一個(gè)真正的系統(tǒng),就必須要為他們添加測(cè)試(但是最后重新開(kāi)始設(shè)計(jì)系統(tǒng))
n 單純的單元測(cè)試無(wú)法保證好的設(shè)計(jì),但它們會(huì)對(duì)設(shè)計(jì)有幫助,會(huì)然設(shè)計(jì)更加簡(jiǎn)單。
不同環(huán)境,就有不同問(wèn)題
“只要代碼能在你的機(jī)器上運(yùn)行就可以了,誰(shuí)會(huì)去關(guān)心她是否可以在其他平臺(tái)上工作,你又不用其他平臺(tái)。”如果廠商或者同事說(shuō)了這樣的套話:“哦,那不會(huì)有什么不同。”你可以打賭,他們錯(cuò)了,只要環(huán)境不同,就哼可能會(huì)有不同的問(wèn)題
Ventkat真正在項(xiàng)目中學(xué)到了這一課,他的一個(gè)同事抱怨說(shuō)mVenkat的代碼失敗了。但奇怪的是,問(wèn)題在于,這于再Venkat機(jī)器上通過(guò)的一個(gè)測(cè)試一模一樣。實(shí)際上,它在一臺(tái)機(jī)器上可以運(yùn)行,在另一臺(tái)機(jī)器上就不工作。
最后,他們終于找到罪魁禍?zhǔn)?#xff1a;一個(gè)NET環(huán)境下的API在Windows XP 和Window3002上的行為不同。平臺(tái)的不同,造成了結(jié)果不一樣
他們算是幸運(yùn)的,能夠偶然發(fā)現(xiàn)這個(gè)問(wèn)題,否則,很可能在產(chǎn)品投入使用的時(shí)候才會(huì)發(fā)現(xiàn)。如果很晚才發(fā)現(xiàn)這個(gè)問(wèn)題,成本會(huì)非常昂貴———現(xiàn)象一下產(chǎn)品發(fā)表之后,才發(fā)現(xiàn)他并不支持應(yīng)該支持的平臺(tái),那會(huì)怎么樣
也許,你會(huì)要求測(cè)試團(tuán)隊(duì)在所有支持的平臺(tái)上進(jìn)行測(cè)試,如果他們的手工精細(xì)測(cè)試,可能并不是最可靠的測(cè)試辦法,我們需要根據(jù)面向開(kāi)發(fā)者的測(cè)試辦法
你已經(jīng)編寫(xiě)了單元測(cè)試,測(cè)試你的代碼,每次在修改或者重構(gòu)代碼的時(shí)候在提交代碼之前,你會(huì)運(yùn)行測(cè)試辦法,那么現(xiàn)在所要做的就是各種支持的平臺(tái)和環(huán)境運(yùn)行這些測(cè)試用例。
如果你的應(yīng)用程序在不同操作系統(tǒng)上運(yùn)行(例如 MacOs,Linux,Windows等),或者一個(gè)操作系統(tǒng)的不同版本(例如:Windows 2000,Windows XP ,Windows 2003 等)你需要測(cè)試所有的操作系統(tǒng)。如果你的應(yīng)用程序要在不同的版本的Java虛擬機(jī)或者不同的NET CLR中運(yùn)行,你也需要測(cè)試他們。
但是,也許你已經(jīng)有時(shí)間壓力了,因此,你怎么可能喲時(shí)間在多個(gè)平臺(tái)上運(yùn)行測(cè)試呢?這就要靠持續(xù)集成來(lái)拯救了。
使用自動(dòng)化會(huì)節(jié)省時(shí)間
Automate to save time
我們?cè)谇懊娴谋3挚梢园l(fā)布中學(xué)過(guò),用一個(gè)持續(xù)集成工具,周期性的從源代碼控制系統(tǒng)中取得代碼,并運(yùn)行代碼,如果有任何測(cè)試失敗了,它會(huì)通知相關(guān)的開(kāi)發(fā)者,通知方式可能是電子郵件,頁(yè)面,RSS Feed,或者其他一些新穎的方式!
要在多個(gè)平臺(tái)上測(cè)試,你只要為每個(gè)平臺(tái)設(shè)置持續(xù)集成系統(tǒng)就行了,但你或者同事提交代碼,測(cè)試會(huì)在每個(gè)平臺(tái)上自動(dòng)運(yùn)行,這樣,提交代碼之后的幾分鐘,你就可以知道它是否可以在不同的平臺(tái)上運(yùn)行!這是多么英明的辦法呀!
構(gòu)建機(jī)器的硬件成本相當(dāng)于開(kāi)發(fā)人員的幾個(gè)小時(shí)而已。如果需要,你甚至可以是用VMware或Virtual PC這樣的虛擬機(jī)產(chǎn)品,在一臺(tái)機(jī)器上運(yùn)行不同版本的操作系統(tǒng) ,VM或CLR
不同環(huán)境有不同問(wèn)題。使用持續(xù)集成工具,在每一種支持的平臺(tái)和環(huán)境中運(yùn)行單元測(cè)試,要積極地尋找問(wèn)題,而不是等問(wèn)題來(lái)找你
切身感受
感覺(jué)就像是單元測(cè)試,非但如此,而且還是跨越不同的世界的單元測(cè)試
平衡的藝術(shù)
n 不硬件比開(kāi)發(fā)人員的時(shí)間更便宜,但如果你有很配置,要支持大量的平臺(tái),可以選擇哪些平臺(tái)需要內(nèi)部測(cè)試。
n 只因?yàn)椴煌臈禹樞?不同的單詞大小寫(xiě)但而過(guò),就能發(fā)現(xiàn)很多的平臺(tái)上的bug。因此,即使運(yùn)行 Solaris的客戶比用Linux的少的很多,你仍然要在兩個(gè)系統(tǒng)上進(jìn)行測(cè)試
n 你不希望因?yàn)橐粋€(gè)錯(cuò)誤而收到5次通過(guò)轟炸(這就像雙重征稅,會(huì)導(dǎo)致電子游戲疲勞癥)。可以設(shè)置一個(gè)主構(gòu)建平臺(tái)或者配置,降低其他的構(gòu)建服務(wù)器的運(yùn)行頻率,這樣在它失敗的時(shí)候,你就喲足夠多的時(shí)間來(lái)修復(fù)構(gòu)建平臺(tái),或者匯總所以錯(cuò)誤報(bào)告信息到一個(gè)地方,進(jìn)行統(tǒng)一處理
自動(dòng)驗(yàn)收測(cè)試
“很好,你現(xiàn)在用單元測(cè)試來(lái)驗(yàn)證代碼是否完成了你期望的行為,發(fā)給客戶吧。我們很快會(huì)知道這是否算是用戶期望的功能。”
你于用戶一起工作,開(kāi)發(fā)他們想要的功能,但現(xiàn)在,你要能確保他們得到的數(shù)據(jù)庫(kù)是正確的,至少在用戶看來(lái)它是正確的。
幾年前,Andy做了一個(gè)項(xiàng)目,在項(xiàng)目中,他們的行業(yè)標(biāo)準(zhǔn)規(guī)定凌晨12:00點(diǎn)式一天的最后一分鐘,在12:01是一天的最早一分鐘(一般情況下,商業(yè)計(jì)算機(jī)系統(tǒng)任務(wù)凌晨11:59是一天的最后一分鐘,12:00是一天的最早的一分鐘)在驗(yàn)收測(cè)試的時(shí)候這個(gè)很小的細(xì)節(jié)導(dǎo)致一個(gè)嚴(yán)重的問(wèn)題——無(wú)法進(jìn)行正確的合計(jì)。
關(guān)鍵業(yè)務(wù)邏輯必須要獨(dú)立精細(xì)嚴(yán)格的測(cè)試,并且最后需要通過(guò)用戶的審批。
但你也不可能拉這用戶,逐一檢查每個(gè)單元測(cè)試運(yùn)行結(jié)果。實(shí)際上你需要能自動(dòng)比較用戶期望和實(shí)際完成的工作
有一個(gè)辦法是可以是驗(yàn)收測(cè)試不同意單元測(cè)試你應(yīng)該讓用戶在不必學(xué)習(xí)編碼的情況下,根據(jù)自己的需要進(jìn)行添加,更新和修改數(shù)據(jù),你有很多方法來(lái)實(shí)現(xiàn)它。
Andy使用了一下架構(gòu),把測(cè)試數(shù)據(jù)放到一個(gè)頁(yè)面文件中并且用戶可以直接修改這些實(shí)際。Venkat實(shí)現(xiàn)Excel做過(guò)類似的事情,根據(jù)環(huán)境的不同,也可以找出一種能然偶那個(gè)號(hào)自然接收的方法(數(shù)據(jù)可以在頁(yè)面的文件Excel 文件,數(shù)據(jù)庫(kù)中)。或者可以考慮選擇一個(gè)現(xiàn)成的測(cè)試工具,它們會(huì)會(huì)你完成很多功能。
FIT,即集成測(cè)試框架,它很實(shí)用,可以更容易的使用HTML表格定義的測(cè)試用例,并比較測(cè)試結(jié)果的數(shù)據(jù)。
中運(yùn)行,你也需要測(cè)試他們。
使用FIT,客戶可以定義帶有新功能的使用樣本,客戶,測(cè)試人員和開(kāi)發(fā)人員(根據(jù)樣本)都可以成績(jī)表格,為代碼描述可能的輸入和輸出值,開(kāi)發(fā)人員會(huì)參照帶有正開(kāi)發(fā)的代碼結(jié)果在FIT表格中的樣本編寫(xiě)測(cè)試代碼,測(cè)試結(jié)果成功或者失敗都會(huì)顯示在HTML教程頁(yè)面中,用戶可以很方便的查閱。
如果領(lǐng)域?qū)<姨峁┝藰I(yè)務(wù)的算法,運(yùn)算后者方程式,為他們實(shí)現(xiàn)一套可以獨(dú)立運(yùn)行的測(cè)試(參考第136頁(yè)習(xí)慣35)。要讓這些測(cè)試都成為測(cè)試套件的一部分,你會(huì)在項(xiàng)目生命周期保持實(shí)現(xiàn)為他們提供正確的答案。
為刻心的業(yè)務(wù)邏輯創(chuàng)建測(cè)試。讓你的客戶單獨(dú)驗(yàn)證這些測(cè)試,要讓他們像一般的測(cè)試一樣可以自動(dòng)運(yùn)行。
切身感受
它像是協(xié)作完成的單元測(cè)試:你仍然是在編寫(xiě)測(cè)試,但從其他人那里獲取答案
平衡的藝術(shù)
n 不是所有客戶都恩能夠給你提供正確的數(shù)據(jù),如果他們已經(jīng)有了正確的數(shù)據(jù),就根本不需要新的系統(tǒng)了。
n 你也許會(huì)在酒系統(tǒng)(運(yùn)行時(shí)電腦系統(tǒng),也是是人工系統(tǒng))中發(fā)現(xiàn)一起根本不知道的bug,或者一起不存在的真正的問(wèn)題
n 是客戶的業(yè)務(wù)邏輯,但是不要陷于無(wú)邊無(wú)際的文檔寫(xiě)作之中。
度量真實(shí)的進(jìn)度
“用自己的時(shí)間表報(bào)告工作進(jìn)度,我們會(huì)用它做項(xiàng)目計(jì)劃,不用管那些時(shí)間的工作時(shí)間,每周填滿40小時(shí)就可以了。”
時(shí)間的消逝(通常很快)可以證明:判斷工作進(jìn)度最后是看實(shí)際花費(fèi)的時(shí)間而不是估計(jì)的時(shí)間。
哦,你說(shuō)早已經(jīng)用時(shí)間進(jìn)行了追蹤。不幸的是,幾乎所有公司的時(shí)間表都是會(huì)工資會(huì)計(jì)準(zhǔn)備的,不是用度量軟件項(xiàng)目的開(kāi)發(fā)進(jìn)度的。例如:如果你工作了60個(gè)小時(shí),也許你的老板會(huì)讓你在時(shí)間表上只填寫(xiě)40個(gè)小時(shí),這是公司會(huì)計(jì)想到的,所以,時(shí)間表很難真實(shí)的反映工作完成的狀況,因此它不可以用來(lái)進(jìn)行羨慕計(jì)劃,評(píng)估或表現(xiàn)評(píng)估。
專注于你的方向
Focus on where your.re going
即使沒(méi)有時(shí)間表,一下開(kāi)發(fā)人員還是很難面對(duì)現(xiàn)實(shí)了解自己的真實(shí)進(jìn)度,你曾經(jīng)聽(tīng)到開(kāi)發(fā)人員報(bào)告一個(gè)任務(wù)完成了80%嗎?然而過(guò)了一天又一天,一周又周,那個(gè)任務(wù)仍然是完成了80%?隨意用一個(gè)比率進(jìn)行度量是沒(méi)有意義的,這就好比說(shuō)80%是對(duì)的(除非你是政客,否則對(duì)和錯(cuò)應(yīng)該是布爾條件)。所以,我們不應(yīng)該去計(jì)算工作完成的百分比,而應(yīng)該測(cè)試還剩下多少工作量沒(méi)有完成。如果你最初估計(jì)這個(gè)任務(wù)需要40個(gè)小時(shí),在開(kāi)發(fā)35個(gè)小時(shí)之后,你認(rèn)為你還需要另外30個(gè)小時(shí)的工作。那就得到了最重要的度量結(jié)果(這里誠(chéng)實(shí)非常重要,隱瞞真相毫無(wú)意義)。
在你最后真正完成一項(xiàng)任務(wù)時(shí),要清楚知道完成這個(gè)任務(wù)真正花費(fèi)的時(shí)間。奇怪的是,它花費(fèi)的時(shí)間很有可能要比最初估計(jì)時(shí)間長(zhǎng)。沒(méi)有關(guān)系,我們希望這能作為下一次的參考,在為下一個(gè)任務(wù)估計(jì)工作量時(shí),可以根據(jù)這次經(jīng)驗(yàn)調(diào)整評(píng)估,如果你低估了一個(gè)任務(wù),評(píng)估師2天,它最后花費(fèi)了6天,那么系數(shù)就是3。除非是異常情況,否則你應(yīng)該對(duì)下一次估計(jì)乘以系數(shù)3。你的評(píng)估會(huì)波動(dòng)一段時(shí)間,有時(shí)候過(guò)低估計(jì),有時(shí)候會(huì)過(guò)高估計(jì)。但隨著時(shí)間的推移,你的評(píng)估會(huì)于事實(shí)接進(jìn)
近,你也會(huì)對(duì)任務(wù)所花費(fèi)的時(shí)間更清楚地認(rèn)識(shí)。
運(yùn)行,你也需要測(cè)試他們。
如果能一直讓下一步工作是可見(jiàn)的,會(huì)助于進(jìn)度度量。組合的做法就是使用待辦事項(xiàng)(backlog)。
待辦事項(xiàng)就是等待完成的任務(wù)列表,當(dāng)一個(gè)任務(wù)被完成了,它就會(huì)在列表中移動(dòng)(邏輯上的,而物理上就是把它沖列表中劃掉或者標(biāo)識(shí)它是完成的狀態(tài))。當(dāng)添加新新任務(wù)的時(shí)候,先排列他們的優(yōu)先級(jí),然后加入到待辦事項(xiàng)中。你也可以由各人的待辦事項(xiàng),當(dāng)前迭代的待辦事項(xiàng)或者整個(gè)項(xiàng)目的待辦事項(xiàng)。
通過(guò)代辦事項(xiàng),就可以隨時(shí)知道下一步最重要的任務(wù)是什么。同事你的評(píng)估技巧也在不停的改進(jìn),你也會(huì)越來(lái)越清楚完成一些任務(wù)的花費(fèi)的時(shí)間。
清楚項(xiàng)目的真實(shí)進(jìn)度,是一項(xiàng)強(qiáng)大的技術(shù)。
度量剩下的工作量。不要用不恰當(dāng)?shù)亩攘縼?lái)欺騙自己或者團(tuán)隊(duì),要評(píng)估那些需要完成的待辦事項(xiàng)。,要讓他們像一般的測(cè)試一樣可以自動(dòng)運(yùn)行。
切身感受
你會(huì)覺(jué)得很舒服,因?yàn)槟愫芮宄男┤蝿?wù)已經(jīng)完成,哪些是還沒(méi)有完成,以及他們的優(yōu)先級(jí)
平衡的藝術(shù)
n 6分鐘作為一個(gè)時(shí)間單位,它的粒度實(shí)在是太細(xì)了,這不是敏捷的做法
n 一周或者一個(gè)月的時(shí)間單元,它的粒度太粗了,這不是敏捷的做法
n 關(guān)注功能,而不是日程表。
n 如果你在一個(gè)項(xiàng)目中花費(fèi)了很多時(shí)間來(lái)了解你所花費(fèi)的時(shí)間,而沒(méi)有足夠的時(shí)間進(jìn)行工作,那么你在了解你所花費(fèi)的時(shí)間上花費(fèi)的時(shí)間久太多了,聽(tīng)懂了嗎
n 一周工作40個(gè)小時(shí),不是說(shuō)你就有40個(gè)小時(shí)的編碼時(shí)間,你需要減少去會(huì)議,電話,電子郵件以其他相關(guān)活動(dòng)的時(shí)間。
傾聽(tīng)用戶的聲音
“用戶就是會(huì)抱怨,這不是你的過(guò)錯(cuò),是用戶太愚蠢了,連使用手冊(cè)都看不懂。它不是bug,只是用戶不明白如何使用而已,他們本應(yīng)該知道更多”。
Andy曾經(jīng)在一家大公司工作過(guò),為高端的Unix工作站開(kāi)發(fā)產(chǎn)品。在這個(gè)環(huán)境中,你不是簡(jiǎn)單的運(yùn)行setup.exe文件或者pkgadd命令,就可以完成軟件的安裝。你必須在工作站上復(fù)制文件并調(diào)整各種設(shè)置。
Andy和他的團(tuán)隊(duì)成員們覺(jué)得一切工作的很順利。直到一天,Andy走過(guò)幾十支持部門的工作間,聽(tīng)到一個(gè)技術(shù)支持工程師對(duì)著電話大笑:“哦,這不是bug,你只是犯了一個(gè)每個(gè)人都會(huì)犯得錯(cuò)誤。”并且,不只是這一個(gè)工程師,整個(gè)部門都在嘲笑這些可憐,天真和愚蠢的客戶。倒霉的客戶必須要配置那些包含以下魔法數(shù)字的模糊系統(tǒng)文件,
否則系統(tǒng)根本運(yùn)行不起來(lái)。系統(tǒng)即沒(méi)有錯(cuò)誤提示消息,也不會(huì) 這是一個(gè)bug
破潰,只是顯示大黑屏和一個(gè)斗大的“退出”按鈕。事實(shí)上,安 this is a bug
裝說(shuō)明書(shū)中有一行提到了這樣的問(wèn)題,但顯然80%的用戶忽略
了這個(gè)信息,因此只能求助公司的技術(shù)支持部門,并遭到他們的嘲笑.
正如我們?cè)诘?28頁(yè)第7章中所說(shuō),當(dāng)出了錯(cuò)誤,你要盡可能地提供詳細(xì)信息.黑屏和含義不明的”退出”按鈕是很不友好的行為.更糟糕的是,在得到用戶反饋的時(shí)候,還嘲笑用戶愚蠢,而不去真正地解決問(wèn)題.
不管它是否是產(chǎn)品的bug,還是文檔的bug,或者是對(duì)用戶社區(qū)理解的bug,它都是團(tuán)隊(duì)的問(wèn)題.而不是用戶的問(wèn)題.
下面是一個(gè)案例是:一個(gè)昂貴的專業(yè)車間的控制系統(tǒng),沒(méi)有任何一個(gè)用戶會(huì)使用.因?yàn)?使用系統(tǒng)的第一步是要輸入用戶名和密碼,進(jìn)行登錄.但這個(gè)車間的大部分工人都是文盲,沒(méi)有人去問(wèn)過(guò)他們,也沒(méi)有去收集他們的反饋.就這樣,為用戶安裝了一個(gè)無(wú)用的系統(tǒng).最后,花費(fèi)巨大的費(fèi)用,開(kāi)發(fā)人員重新開(kāi)發(fā)了一個(gè)基于圖片的使用界面.
我們花費(fèi)了很大的精力從單元測(cè)試之類的代碼中獲得反饋,但卻容易忽略最終用戶的反饋.你不僅需要和真實(shí)用戶(不是他們的經(jīng)理,也不是業(yè)務(wù)分析師之類的代理人)進(jìn)行交談,還分需要耐心地傾聽(tīng).
即使他們說(shuō)的內(nèi)容很傻!
每一個(gè)抱怨的背后都隱藏了一個(gè)事實(shí).找出真相,修復(fù)真正的問(wèn)題.
切身感受
對(duì)客戶的那些愚蠢抱怨,你既不會(huì)生氣,也不會(huì)輕視,你會(huì)查看一下,找出背后真正的問(wèn)題.
平衡的藝術(shù):
n 沒(méi)有愚蠢的用戶.
n 只有愚蠢,自大的開(kāi)發(fā)人員.
n “它就是這樣的.”這不是一個(gè)好的答案.
n 如果代碼問(wèn)題解決不了,也許可以考慮通過(guò)修改文檔或者培訓(xùn)來(lái)彌補(bǔ).
n 你的用戶有可能會(huì)閱讀所有的文檔,記住其中的所有內(nèi)容.但也可能不會(huì).
第6章 敏捷編碼
任何一個(gè)笨蛋都能夠讓事情變得越來(lái)越笨重,越來(lái)越極端.需要天才的指點(diǎn)以及許多的勇氣,才能讓事情向相反的方向發(fā)展.
-John Dryden, 書(shū)信集10: 至Congreve
新項(xiàng)目剛開(kāi)始著手開(kāi)發(fā)時(shí),它的代碼很容易理解和上手.然而,隨著開(kāi)發(fā)過(guò)程的推進(jìn),項(xiàng)目不知不覺(jué)中演變?yōu)橐粋€(gè)龐然怪物.發(fā)展到最后,往往需要投入更多的精力,人力和物力來(lái)讓它繼續(xù)下去.
開(kāi)始看起來(lái)非常正常的項(xiàng)目,是什么讓它最終變得難以掌控?開(kāi)發(fā)人員在完成任務(wù)時(shí),可能會(huì)難以底擋誘惑為節(jié)省時(shí)間而走”捷徑”.然而,這些”捷徑”往往只會(huì)推遲問(wèn)題的爆發(fā)時(shí)間,而不是把它徹底解決掉(如同第15頁(yè)習(xí)慣2中的情況一樣).當(dāng)項(xiàng)目時(shí)間上的壓力增加時(shí),問(wèn)題最終還是會(huì)在項(xiàng)目團(tuán)隊(duì)面前出現(xiàn),讓大家心煩意亂.
如何保證項(xiàng)目開(kāi)發(fā)過(guò)程中壓力正常,而不是在后期面對(duì)過(guò)多的壓力,以致噩夢(mèng)連連呢?最簡(jiǎn)單的方式,就是在開(kāi)發(fā)過(guò)程中便細(xì)心”照看”代碼.在編寫(xiě)代碼時(shí),每天付出一點(diǎn)小的努力,可以避免代碼”腐爛”,并且保證應(yīng)用程序不至變得難以理解和維護(hù).
開(kāi)發(fā)人員使用本章的實(shí)踐習(xí)慣,可以保證開(kāi)發(fā)出的代碼無(wú)論是在項(xiàng)目進(jìn)行中還是在項(xiàng)目完成后,都易于理解,擴(kuò)展和維護(hù).這些習(xí)慣會(huì)幫助你對(duì)代碼進(jìn)行”健康檢查”,以防止它們變成龐然怪物.
首先,第100頁(yè)中的習(xí)慣是:代碼要清晰地表達(dá)意圖.這樣的代碼清晰易懂,僅憑小聰明寫(xiě)出的程序很難維護(hù).注釋可以幫助理解,也可能導(dǎo)致不好的干擾,應(yīng)該總是用代碼溝通(見(jiàn)105頁(yè)).在工程項(xiàng)目中沒(méi)有免費(fèi)的午餐,開(kāi)發(fā)人員必須判斷哪些東西更加重要,每個(gè)決策會(huì)造成的后果,也就是說(shuō)要?jiǎng)討B(tài)評(píng)估取舍(見(jiàn)第110頁(yè))以得到最佳的決策.
項(xiàng)目是以增量方式進(jìn)行開(kāi)發(fā)的,寫(xiě)程序時(shí)也應(yīng)該進(jìn)行增量式編程(見(jiàn)第113頁(yè)).在編寫(xiě)代碼的時(shí)候,要想保持簡(jiǎn)單很難做到---實(shí)際上,想寫(xiě)出簡(jiǎn)單的代碼要遠(yuǎn)比寫(xiě)出令人厭惡的,過(guò)分復(fù)雜的代碼難得多,不過(guò)這樣做絕對(duì)值得,見(jiàn)第115頁(yè).
我們將在第117頁(yè)談到,良好的面向?qū)ο笤O(shè)計(jì)原則建議:應(yīng)該編寫(xiě)內(nèi)聚的代碼.要保持代碼條理清晰,應(yīng)該遵循如第121頁(yè)上所述的習(xí)慣:告知,不要詢問(wèn).最后,通過(guò)設(shè)計(jì)能夠根據(jù)契約進(jìn)行替換的系統(tǒng)(見(jiàn)124頁(yè)),可以在不確定的未來(lái)中保持代碼的靈活性.
25 代碼要清晰地表達(dá)意圖
“可以工作而且易于理解的代碼當(dāng)然好,但是讓人覺(jué)得聰明更加重要.別人給你錢是因?yàn)槟隳X子好使,讓我們看看你到底有多聰明.”
我們大概都見(jiàn)過(guò)不少難以理解和維護(hù)的代碼.而且(最壞的是)還有錯(cuò)誤.當(dāng)開(kāi)發(fā)人員們像一群旁觀見(jiàn)到UFO一樣圍在代碼四周,同樣也感到恐懼,困惑與無(wú)助時(shí),這個(gè)代碼的質(zhì)量就可想而知了.如果沒(méi)有人理解一段代碼的工作方式,那這段代碼還有什么用呢?
開(kāi)發(fā)代碼時(shí),應(yīng)該更注重可讀性,而不是只圖自己方便.代碼閱讀的次數(shù)要遠(yuǎn)遠(yuǎn)超過(guò)編寫(xiě)的次數(shù)和,所以在編寫(xiě)的時(shí)候值得花點(diǎn)功夫讓它讀起業(yè)更加簡(jiǎn)單.實(shí)際上,從衡量標(biāo)準(zhǔn)上來(lái)看,代碼清晰程度的優(yōu)先級(jí)應(yīng)該排在執(zhí)行效率之前.
例如,如果默認(rèn)參數(shù)或可選參九會(huì)影響代碼可讀性,使其更難以理解和調(diào)試,那最好明確地指明參數(shù),而不是在以后讓人覺(jué)得迷惑。
在改動(dòng)代碼以修復(fù)bug或者添加新功能時(shí),應(yīng)該有條不紊地進(jìn)行。首先,應(yīng)該理解代碼做什么,它是如何做的。接下來(lái),搞清楚將要改變哪些部分,然后著手修改并進(jìn)行測(cè)試。作為第1步的理解代碼,往往是最難的。如果別人給你的代碼很容易理解,接下來(lái)的工作就省心多了。要敬重這個(gè)黃金法則,你欠他們一份情,因此也要讓你自己的代碼簡(jiǎn)單、便于閱讀。
明白地告訴閱讀程序的人,代碼都做了什么,這是讓其便于理解的一種方式。讓我們看一些例子。
coffeeShop.PlaceOrder(2);
通過(guò)閱讀上面的代碼,可以大致明白這是要在咖啡店中下一個(gè)訂單。但是,2到底是什么意思?是意味著要兩杯咖啡?要再加兩次?還是杯子的大小?要想搞清楚,唯一的方式就是去看方法定義或者文檔,因?yàn)檫@段代碼沒(méi)有做到清晰易懂。
所以我們不妨添加一些注釋。
coffeeShop.PlaceOrder(2/*large cup*/);
現(xiàn)在看起來(lái)好一點(diǎn)了,不過(guò)請(qǐng)注意,注釋有時(shí)候是為了幫寫(xiě)得不好的代碼補(bǔ)漏(見(jiàn)第105頁(yè)習(xí)慣26:用代碼溝通)。
Java5與.NET中有枚舉值的概念,我們不妨使用一下。使用C#,我們可以定義一個(gè)名為CoffeeCupSize的枚舉,如下所示。
public enum CoofeeCupSize
{Small,Medium,Large
}
接下來(lái)就可以用它來(lái)下單要咖啡了。
coffeeShop.PlaceOrder(CoffeeCupSize.Largxe);
這段代碼就很明白了,我們是要一個(gè)大杯的咖啡。
作為一個(gè)開(kāi)發(fā)者,應(yīng)該時(shí)常提醒自己是否有辦法讓寫(xiě)出的代碼更容易理解。下面是另一個(gè)例子。
Line 1 public int compute(int val)
{int result = val<<1;//…more code…return result;
}
但對(duì)沒(méi)有類似背景的人們來(lái)說(shuō),又會(huì)如何—他們能明白嗎?也許團(tuán)隊(duì)中有一些剛剛轉(zhuǎn)行做開(kāi)發(fā)、沒(méi)有太多經(jīng)驗(yàn)的成員。他們會(huì)撓頭不已,直到把頭發(fā)抓下來(lái)。代碼執(zhí)行效率也許很高,但是缺少明確的意圖和表現(xiàn)力。
用位移做乘法,是在對(duì)代碼進(jìn)行不必要且危險(xiǎn)的性能優(yōu)化。Result-val*2看起來(lái)更加清晰
也可以達(dá)到目的,而且對(duì)于某種給定的編譯器來(lái)說(shuō),可能效率更高(懂得丟棄,見(jiàn)34頁(yè)習(xí)慣7)。不要表現(xiàn)得好像很聰明似的,要遵循PIE原則:代碼要清晰的表達(dá)意圖。
要違反了PIE原則,造成的問(wèn)題就不只是代碼可讀性那么簡(jiǎn)單了——它會(huì)影響到代碼的正確性。下列代碼是一個(gè)C#方法,試圖同步對(duì)CoffeeMaker中MakeCoffee()方法進(jìn)行調(diào)用。
Public void MakeCoffee
{Lock(this)
{//…operation
}
}
這個(gè)方法的作者想設(shè)置一個(gè)臨界區(qū)(critical section)——任何時(shí)候最多只能有一個(gè)線程來(lái)執(zhí)行操作中的代碼,要達(dá)到這個(gè)目的,作者在CoffeeMaker實(shí)例中聲明了一個(gè)鎖。一個(gè)線程只有獲得這個(gè)鎖,才能執(zhí)行這個(gè)方法。(在JAVA中,會(huì)使用synchronized而不是lock,不過(guò)想法是一樣的。)
對(duì)于java或NET程序員來(lái)說(shuō),這樣寫(xiě)順理成章,但是其中有兩個(gè)小問(wèn)題。首先,鎖的使用影響范圍過(guò)大;其次,對(duì)一個(gè)全局可見(jiàn)的對(duì)象使用了鎖,我們進(jìn)一步來(lái)看看這兩個(gè)問(wèn)題。
假設(shè)CoffeeMaker 同時(shí)可以提供熱水,因?yàn)橛行┤讼M缟夏軌蛳碛靡稽c(diǎn)伯爵紅茶。我想同步GetWater()方法,因此調(diào)用其中的lock(this)。這會(huì)同步任何在CoffeeMaker上使用lock的代碼,也就意味著不能同時(shí)制作咖啡以及獲取熱水。這是開(kāi)發(fā)者原本的意圖嗎?還是鎖的影響范圍太大了?通過(guò)閱讀代碼并不能明白這一點(diǎn),使用代碼的人也就迷惑不已了。
同時(shí),MakeCoffee()方法的實(shí)現(xiàn)在CoffeeMaker對(duì)象上聲明了一個(gè)鎖,而應(yīng)用的其他部分都可以訪問(wèn)CoffeeMaker對(duì)象。如果在一個(gè)線程中鎖定了CoffeeMaker對(duì)象實(shí)例,然后在另外一個(gè)線程中調(diào)用那個(gè)實(shí)例之上的MakeCoffee()方法呢?最好的狀況也會(huì)執(zhí)行效率很差,最壞的狀況會(huì)帶來(lái)死鎖。
讓我們?cè)谶@段代碼上應(yīng)用PIE原則,通過(guò)修改讓它變得更加明確吧。我們不希望同時(shí)有兩個(gè)或更多的線程來(lái)執(zhí)行MakeCoffee()方法。那為什么不能為這個(gè)目的創(chuàng)建一個(gè)對(duì)象并鎖定它呢?
private Object makeCoffeeLock = new Object();
public void MakeCoffee()
{Lock(makeCoffeeLock){// … operation
}
}
這段代碼解決了上面的兩個(gè)問(wèn)題 — 我們通過(guò)指定一個(gè)外部對(duì)象來(lái)進(jìn)行同步操作,而且更加明確地表達(dá)了意圖。
在編寫(xiě)代碼時(shí),應(yīng)該使用語(yǔ)言特性來(lái)提升表現(xiàn)力。使用方法名來(lái)傳達(dá)意向,對(duì)方法參數(shù)的命名要幫助讀者理解背后的想法。異常傳達(dá)的信息是哪些可能會(huì)出現(xiàn)問(wèn)題,以及如何進(jìn)行防御式編程,要正確地使用和命名異常。好的編碼規(guī)范可以讓代碼變得易于理解,同時(shí)減少不必要的注釋和文檔。
要編寫(xiě)清晰的而不是討巧的代碼。向代碼讀者明確表明你的意圖。可讀性差的代碼一點(diǎn)都不聰明。
切身感受應(yīng)該讓自己或團(tuán)隊(duì)的其他任何人,可以讀懂自己一年前寫(xiě)的代碼,而且只讀一遍就知道它的運(yùn)行機(jī)制。
平衡的藝術(shù)
□ 現(xiàn)在對(duì)你顯而易見(jiàn)的事情,對(duì)別人可能并非如此,對(duì)于一年以后的你來(lái)說(shuō),也不一定顯而易見(jiàn)。不妨將代碼視作不知道會(huì)在未來(lái)何時(shí)打開(kāi)的一個(gè)時(shí)間膠囊。
□ 不要明日復(fù)明日。如果現(xiàn)在不做的話,以后你也不會(huì)做的。
□ 有意圖的編程并不是以為著創(chuàng)建更多的類或者類型。這不是進(jìn)行過(guò)分抽象的理由。
□ 使用符合當(dāng)時(shí)情形的耦合。例如,通過(guò)散列表進(jìn)行松耦合,這種方式適用于在實(shí)際狀況中就是松耦合的組件。不要使用散列表存儲(chǔ)緊密耦合的組件,因?yàn)檫@樣沒(méi)有明確表示出你的意圖。
26.用代碼溝通
“如果代碼太雜亂以至于無(wú)法閱讀,就應(yīng)該使用注釋來(lái)說(shuō)明.精確解釋代碼做了什么,每行代碼應(yīng)該加注釋,不用關(guān)為什么要這樣編碼,只要告訴我們到底是怎樣做的就好了.”
通常程序員都很討厭文檔,這是因?yàn)榇蟛糠治臋n都與代碼沒(méi)有什么關(guān)系,并且越來(lái)越難保證其符合目的的最新情況.這不是違反了DRY原則(不要重復(fù)你自己Don’t Repeat Yourself ,見(jiàn)[HTOO]),還會(huì)產(chǎn)生是人誤解的文檔,這還不如沒(méi)有文檔.
建立代碼文檔無(wú)外乎兩種方式.利用代碼本身,利用注釋來(lái)溝通代碼之外的問(wèn)題..
如果必須通讀一個(gè)方法的代碼才能了解它在做了什么
,那么開(kāi)發(fā)人員要投入大量的時(shí)間和精力才能完成它. 不需要注釋來(lái)包裹你
的代碼的代碼反過(guò)來(lái)講,只需短短幾行注 釋說(shuō)明方法 Don't comment to cover up
行為,就可以讓生活變得輕松許多.開(kāi)發(fā)人員可以很快
了解它的意圖.它的期待結(jié)果,以及應(yīng)該注意之處------這可省了你不少勁兒.
應(yīng)該文檔化你所有的代碼嗎?在某種程度上說(shuō),是的.但這并不意味著要注釋絕大部分代碼,特別是在方法體內(nèi)部.源代碼可以被讀懂,不是因?yàn)槠渲械淖⑨?而應(yīng)該是由于本身優(yōu)雅而清晰------變量名運(yùn)用正確.空格使用得當(dāng),邏輯分離清晰,以及表達(dá)式非常簡(jiǎn)潔.
如何命名很重要,程序元素的命名是代碼讀者必須的部分.通過(guò)使用細(xì)心挑選的名稱,可以向閱讀者傳遞大量的意圖和信息.反過(guò)來(lái)講,使用人造的命名范式會(huì)讓代碼難以閱讀和理解.這些范式中包括的底層數(shù)據(jù)類型信息.會(huì)硬編碼在變量名和方法名中.形成脆弱,僵化代碼,并會(huì)在將來(lái)造成麻煩.
使用細(xì)心 挑選的名稱和清晰的執(zhí)行路徑.代碼幾乎不需要注釋.實(shí)際上,當(dāng)Andy和 Dave Thomas聯(lián)手寫(xiě)作第一本關(guān)于Ruby編程語(yǔ)言的書(shū)籍時(shí)(即參考文獻(xiàn)),他們只要閱讀將會(huì)在Ruby解釋器中執(zhí)行幾行代碼.幾乎就可以把整個(gè)Ruby語(yǔ)言的相關(guān)細(xì)節(jié)記錄下來(lái).代碼能夠自解釋.而不是依懶注釋.是一件很好的事情,Ruby在創(chuàng)建者松本行弘是日本人.而Andy和Dave除了sukiyaki和sake之外一句日語(yǔ)都不會(huì).
如何界定一個(gè)好的命名呢?良好的命名可以相讀者提供大量的正確的信息.不好的命名不會(huì)傳達(dá)任何的信息.,糟糕的命名則會(huì)傳遞錯(cuò)誤的信息..
例如:,一個(gè)命名為readAccount()的方法實(shí)際所做的卻是向硬盤寫(xiě)入地址信息.這樣的命名則是被認(rèn)為是最糟糕的(是的這確實(shí)發(fā)生過(guò),參見(jiàn)[HTOO]).
Foo是一個(gè)具有歷史意義,很棒的臨時(shí)變量名稱.但是它沒(méi)有傳遞作者的任何意圖.要盡量的避免這種神秘的變量名.不是說(shuō)命名短小就等于神秘.在許多的編程語(yǔ)言中.通常使用i來(lái)表示循環(huán)索引變量,s常被用來(lái)表示一個(gè)字符串.這在許多的語(yǔ)言中都是慣用的用法.雖然很短小.但是并不神秘.在這些壞境中使用s作為循環(huán)索引變量.可真不是什么好的主意,名為indexvar的變量也同樣不好.不必費(fèi)盡心機(jī)去用繁復(fù)冗長(zhǎng)的名字替換大家已習(xí)慣的名稱.
.對(duì)于顯而易見(jiàn)的代碼,增加注釋,也會(huì)有同樣的問(wèn)題,比如在一個(gè)類的構(gòu)造器方法后面加這是//Constructor就多此一舉,但是很不幸,這種注釋很常見(jiàn)---通常是用于熱心的IDE插入的.最好的狀況下.他不過(guò)是為代碼添加.了“噪音”.最壞的情況下,隨著時(shí)間的推進(jìn)這些注釋則會(huì)過(guò)時(shí),變得不在正確.
許多的注解沒(méi)有傳遞任何有意義的信息.例如,對(duì)于passthorugh()方法它的注釋是”這個(gè)方法允許你傳遞”,但是讀者能從中的到什么信息呢?這種注釋只會(huì)分散注意力.而且很容易失去效性[假使方法最后方法有被命名為sendToHost()]
注釋可以用來(lái)為讀者指定一條正確的代碼訪問(wèn)路線圖.為代碼中的每類或者模塊添加一個(gè)短小的描述.說(shuō)明其中的目的以及是否有任何特別的需求.對(duì)于類中的每個(gè)方法可能要說(shuō)明下列信息.
¨ 目的: 為什么需要這個(gè)方法?
¨ 需求(前置條件): 方法需要什么樣的輸入,對(duì)象必須處于何種狀態(tài).才能讓這個(gè)方法工作.
¨ 承若(后置條件): 方法成功執(zhí)行后,對(duì)象處于什么樣的狀態(tài).有那些返回值.
¨ 異常: 可能會(huì)發(fā)生什么樣的問(wèn)題.會(huì)拋出什么樣的異常.
要感謝如RDoc,javadoc和ndoc這樣的工具,使用他們可以很方便的直接從代碼注釋創(chuàng)建有用的,格式優(yōu)美的文檔.這些工具抽取注釋,并生成樣式漂亮且?guī)в谐溄拥?#xff28;TML輸出.
使用注釋溝通:使用細(xì)心的選擇的,有意義的命名.用只是描述代碼的意圖和約束.注釋不能代替優(yōu)秀的代碼.
切身感受
注釋就像是可以幫助你的好朋友,可以先閱讀注釋,然后快速瀏覽代碼.從而完全理解它做了什么.以及為什么這樣做?
平衡的藝術(shù)
¨ pascal定理的創(chuàng)始人Blaise Pascal曾說(shuō),他總是沒(méi)有時(shí)間寫(xiě)短息.所以只好寫(xiě)長(zhǎng)信.請(qǐng)?jiān)捇〞r(shí)間去寫(xiě)簡(jiǎn)單扼要的注釋吧!
¨ 在代碼可以傳遞意圖的地方不要寫(xiě)注釋.
¨ 解釋代碼做了什么的注釋用處不那么大.相反,注釋要說(shuō)明什么為什么會(huì)這樣寫(xiě)代碼.
¨ 當(dāng)中寫(xiě)方法時(shí),保留描述原有的方法意圖和約束的注釋.
27.動(dòng)態(tài)評(píng)估取舍
“性能、生產(chǎn)力、優(yōu)雅、成本以及上市時(shí)間,在軟件開(kāi)發(fā)過(guò)程中都是至關(guān)重要的因素。每一項(xiàng)都必須達(dá)到最理想狀態(tài)。”
你可曾經(jīng)身處這樣的團(tuán)隊(duì):管理層和客戶將很大一部分注意力都放在應(yīng)用的界面展示上。也有這樣的團(tuán)隊(duì),其客戶認(rèn)為性能表現(xiàn)非常重要。在團(tuán)隊(duì)中,你可能會(huì)發(fā)現(xiàn),有這樣一個(gè)開(kāi)發(fā)主管或者架構(gòu)師,他會(huì)強(qiáng)調(diào)遵守“正確”的范式比其他任何事情都重要。對(duì)任何單個(gè)因素如此獨(dú)斷地強(qiáng)調(diào),而不考慮它是否是項(xiàng)目成功的必要因素,必然因素,必然導(dǎo)致災(zāi)難的發(fā)生。
強(qiáng)調(diào)性能的重要性情有可原,因?yàn)閻毫拥男阅鼙憩F(xiàn)會(huì)讓一個(gè)應(yīng)用在市場(chǎng)上鎩羽而歸。然而,如果應(yīng)用的性能已經(jīng)足夠好了,還有必要繼續(xù)投入精力讓其運(yùn)行得更快一點(diǎn)嗎?大概不用了吧。一個(gè)應(yīng)用還有很多其他方面的因素同樣重要。與其發(fā)費(fèi)時(shí)間去提升千分之一的性能表現(xiàn),也許減少開(kāi)發(fā)投入,降低成本,并盡快讓?xiě)?yīng)用程序上市銷售更有價(jià)值。
舉例來(lái)說(shuō),考慮一個(gè)必須要與遠(yuǎn)程Windows服務(wù)器進(jìn)行通訊的.NET Windows應(yīng)用程序。可以選擇使用.NET Remoting技術(shù)或Web Service來(lái)實(shí)現(xiàn)這個(gè)功能。現(xiàn)在,針對(duì)使用Web Service的提議,有些開(kāi)發(fā)者會(huì)說(shuō):“我們要在Windows之間進(jìn)行通信,通常此類情況下,推薦使用.NET Remoting。而且,Web Service很慢,我們會(huì)遇到性能問(wèn)題。”嗯,一般來(lái)說(shuō)確實(shí)是這樣。
然而,在這個(gè)例子中,使用Web Service很容易開(kāi)發(fā)。對(duì)Web Service的性能測(cè)試表明XML文檔很小,并且相對(duì)應(yīng)用程序自己的響應(yīng)時(shí)間來(lái)講,花在創(chuàng)建和解析XML上的時(shí)間幾乎可以忽略不計(jì)。使用Web Service不但可以在短期內(nèi)節(jié)省開(kāi)發(fā)時(shí)間,且在此后團(tuán)隊(duì)被迫使用第三方提供的服務(wù)時(shí),Web Service也是個(gè)明智的選擇。
考慮這樣一個(gè)應(yīng)用,從數(shù)據(jù)庫(kù)中讀取數(shù)據(jù),并以表格方式顯示。你可以使用一種優(yōu)雅的、面向?qū)ο蟮姆绞?#xff0c;從數(shù)據(jù)庫(kù)中去數(shù)據(jù),創(chuàng)建對(duì)象,再將它們返回給UI層。在UI層中,你再?gòu)膶?duì)象中拆分出數(shù)據(jù),并組織為表格方式顯示。除了看起來(lái)優(yōu)雅之外,這樣做還有什么好處嗎?
也許你只需要讓數(shù)據(jù)層返回一個(gè)數(shù)據(jù)集(dataset)或數(shù)據(jù)集合,然后用表格顯示這些數(shù)據(jù)即可。這樣還可以避免對(duì)象創(chuàng)建和銷毀所耗費(fèi)的資源。如果需要的只是數(shù)據(jù)展示,為什么要?jiǎng)?chuàng)建對(duì)象去自找麻煩呢?不按書(shū)上說(shuō)的OO方式來(lái)做,可以減少投入,同時(shí)獲得性能上的提升。當(dāng)然,這種方式有很多缺點(diǎn),但問(wèn)題的關(guān)鍵是要多長(zhǎng)個(gè)心眼兒,而不是總按照習(xí)慣性的思路去解決問(wèn)題。
總而言之,要想讓?xiě)?yīng)用成功,降低開(kāi)發(fā)成本與縮短上市時(shí)間,二者的影響同樣重要。由于計(jì)算機(jī)硬件價(jià)格日益便宜,處理速度日益加快,所以可在硬件上多投入以換取性能的提升,并將節(jié)省下來(lái)的時(shí)間放在應(yīng)用的其他方面。
當(dāng)然,這也不完全對(duì)。如果硬件需求非常龐大,需要一個(gè)巨大的計(jì)算機(jī)網(wǎng)格以及眾多的支持人員才能維持其正常運(yùn)轉(zhuǎn)(比如類似Google那樣的需求),那么考慮就要向天平的另一端傾斜了。
但是誰(shuí)來(lái)最終判定性能表現(xiàn)已經(jīng)足夠好,或是應(yīng)用的發(fā)展已經(jīng)足夠“炫”了呢?客戶或是利益相關(guān)者必須進(jìn)行評(píng)估,并做出相關(guān)決定(見(jiàn)第45頁(yè)習(xí)慣10)。如果團(tuán)隊(duì)認(rèn)為性能上還有提升的空間,或者覺(jué)得可以讓某些界面看起來(lái)更吸引人,那么就去咨詢一下利益相關(guān)者,讓他們決定應(yīng)將重點(diǎn)放在哪里。
沒(méi)有適應(yīng)所有狀況的最佳解決方案。你必須對(duì)手上 沒(méi)有最佳解決方案
的問(wèn)題進(jìn)行評(píng)估,并選出最合適的解決方案。每個(gè) No best solution
設(shè)計(jì)都是針對(duì)特定問(wèn)題的 — 只有明確地進(jìn)行評(píng)
估和權(quán)衡,才能得出更好的解決方案。動(dòng)態(tài)評(píng)估權(quán)衡。考慮性能、便利性、生產(chǎn)力、成本和上市時(shí)間。如果性能表現(xiàn)足夠了,就將注意力放在其他因素上。不要為了感覺(jué)上的性能提升或者設(shè)計(jì)的優(yōu)雅,而將設(shè)計(jì)復(fù)雜化。
切身感受即使不能面面俱到,你也應(yīng)該覺(jué)得已經(jīng)得到了最重要的東西 — 客戶認(rèn)為有價(jià)值的特性。
平衡的藝術(shù)
□ 如果現(xiàn)在投入額外的資源和精力,是為了將來(lái)可能得到的好處,要確認(rèn)投入一定要得到回報(bào)(大部分情況下,是不會(huì)有回報(bào)的)。
□ 真正的高性能系統(tǒng),從一開(kāi)始設(shè)計(jì)時(shí)就在向這個(gè)方向努力。
□ 過(guò)早的優(yōu)化是萬(wàn)惡之源。
□ 過(guò)去用過(guò)的解決方案對(duì)當(dāng)前的問(wèn)題可能適用,也可能不適用。不要事先預(yù)設(shè)結(jié)論,先看看現(xiàn)在是什么狀況。
28.增量式編程
“真正的程序員寫(xiě)起代碼來(lái),一干就是幾個(gè)小時(shí),根本不停,甚至連頭都不抬。不要停下來(lái)去編譯你的代碼,只要一直往下寫(xiě)就好了!”
當(dāng)你開(kāi)車進(jìn)行長(zhǎng)途旅行時(shí),兩手把住方向盤,固定在一個(gè)位置,兩眼直盯前方,油門一踩到底幾個(gè)小時(shí),這樣可能嗎?當(dāng)然不行了,你必須掌控方向,必須經(jīng)常注意交通狀況,必須檢查油量表,必須停車加油、吃飯,準(zhǔn)備其它必需品,以及諸如此類的活動(dòng)。
如果不對(duì)自己編寫(xiě)的代碼進(jìn)行測(cè)試,保證沒(méi)有問(wèn)題,就不要連續(xù)幾個(gè)小時(shí),甚至連續(xù)幾分鐘進(jìn)行編程。相反,應(yīng)該采用增量式的編程方式。增量式編程可以精煉并結(jié)構(gòu)化你的代碼。代碼被復(fù)雜化、變成一團(tuán)亂麻的幾率減少了。所開(kāi)發(fā)的代碼基于即時(shí)的反饋,這些反饋來(lái)自于小步幅方式編寫(xiě)代碼和測(cè)試的過(guò)程。
采取增量式編程和測(cè)試,會(huì)傾向于創(chuàng)建更小的方法和更具有內(nèi)聚性的類。你不是在埋頭盲目地一次性編寫(xiě)一大堆代碼。相反,你會(huì)經(jīng)常評(píng)估代碼質(zhì)量,并不時(shí)地進(jìn)行許多小調(diào)整,而不是一次修改許多東西。
在編寫(xiě)代碼的時(shí)候,要經(jīng)常留心可以改進(jìn)的微小方面。這可能會(huì)改善代碼的可讀性。也許你會(huì)發(fā)現(xiàn)可以把一個(gè)方法拆成幾個(gè)更小的方法,使其變得更易于測(cè)試。在重構(gòu)的原則指導(dǎo)下,可以做出許多細(xì)微改善(見(jiàn)Martin Fowler的《重構(gòu):改善既有代碼的設(shè)計(jì)》[FBB+99]-書(shū)中的相關(guān)討論)。可以使用測(cè)試優(yōu)先開(kāi)發(fā)方式(見(jiàn)第82頁(yè)習(xí)慣20),作為強(qiáng)制進(jìn)行增量編程的方式。關(guān)鍵在于持續(xù)做一些細(xì)小而有用的事情,而不是做一段長(zhǎng)時(shí)間的編程或重構(gòu)。
這就是敏捷的方式。
在很短的編輯/構(gòu)建/測(cè)試循環(huán)中編寫(xiě)代碼。這要比花費(fèi)長(zhǎng)時(shí)間僅僅做編寫(xiě)代碼的工作好得多。可以創(chuàng)建更加清晰、簡(jiǎn)單、易于維護(hù)的代碼。
切身感受
在寫(xiě)了幾行代碼之后,你會(huì)迫切地希望進(jìn)行一次構(gòu)建/測(cè)試循環(huán)。在沒(méi)有得到反饋時(shí),你不想走得太遠(yuǎn)。
平衡的藝術(shù)
□ 如果構(gòu)建和測(cè)試循環(huán)花費(fèi)的時(shí)間過(guò)長(zhǎng),你就不會(huì)希望經(jīng)常運(yùn)行它們了。要保證測(cè)試可以快速運(yùn)行。
□ 在編譯和測(cè)試運(yùn)行中,停下來(lái)想一想,并暫時(shí)遠(yuǎn)離代碼細(xì)節(jié),這是保證不會(huì)偏離正確方向的好辦法。
□ 要休息的話,就要好好休息。休息時(shí)請(qǐng)遠(yuǎn)離鍵盤。
□ 要像重構(gòu)你的代碼那樣,重構(gòu)你的測(cè)試,而且要經(jīng)常重構(gòu)測(cè)試。
29.保持簡(jiǎn)單“軟件是很復(fù)雜的東西。隨便哪個(gè)笨蛋都可以編寫(xiě)出簡(jiǎn)單、優(yōu)雅的軟件。通過(guò)編寫(xiě)史上最復(fù)雜的程序,你將會(huì)得到美譽(yù)和認(rèn)可,更不用提保住你的工作了。”
也許你看過(guò)這樣一篇文章,其中提到了一個(gè)設(shè)計(jì)想法,表示為一個(gè)帶有花哨名稱的模式。放下雜志,眼前的代碼似乎馬上就可以用到這種模式。這時(shí)要捫心自問(wèn),是不是正的需要用它,以及它將如何幫你解決眼前的問(wèn)題。問(wèn)問(wèn)自己,是不是特定的問(wèn)題強(qiáng)迫你使用這個(gè)解決方案。不要讓自己被迫進(jìn)行過(guò)分設(shè)計(jì),也不要將代碼過(guò)分復(fù)雜化。
Andy曾經(jīng)認(rèn)識(shí)一個(gè)家伙,他對(duì)設(shè)計(jì)模式非常著迷,想把它們?nèi)加闷饋?lái)。有一次,要寫(xiě)一個(gè)大概幾百行代碼的程序。在被別人發(fā)現(xiàn)之前,他已經(jīng)成功地將GoF那本書(shū)[FHJV95]中的17個(gè)模式,都運(yùn)用到那可憐的程序中。
這不應(yīng)該是編寫(xiě)敏捷代碼的方式。
問(wèn)題在于,許多開(kāi)發(fā)人員傾向于將投入的努力與程序復(fù)雜性混同起來(lái)。如果你看到別人給出的解決方案,并評(píng)價(jià)說(shuō)“非常簡(jiǎn)單且易于理解”,很有可能你會(huì)讓設(shè)計(jì)者不高興。許多開(kāi)發(fā)人員以自己程序的復(fù)雜性為榮,如果能聽(tīng)到說(shuō):“Wow,這很難,一定是花了很多時(shí)間和精力才做出來(lái)的吧。”他們就會(huì)面帶自豪的微笑了。其實(shí)應(yīng)當(dāng)恰恰相反,開(kāi)發(fā)人員更應(yīng)該為自己能夠創(chuàng)建出一個(gè)簡(jiǎn)單并且可用的設(shè)計(jì)而驕傲。
“簡(jiǎn)單性”這個(gè)詞匯被人們大大誤解了(在軟件開(kāi) 簡(jiǎn)單不是簡(jiǎn)陋
發(fā)工作以及人們的日常生活中,皆是如此)。它并 Simple is not simplistic
不意味著簡(jiǎn)陋、業(yè)余或是能力不足。恰恰相反,相
比一個(gè)過(guò)分復(fù)雜、拙劣的解決方案,簡(jiǎn)單的方案通常更難以獲得。
簡(jiǎn)單性,在編程或是寫(xiě)作中,就像是廚師的收汁調(diào)料。從大量的葡萄酒、主料和配料開(kāi)始,你小心地進(jìn)行烹調(diào),到最后得到了最濃縮的精華部分。這就是好的代碼應(yīng)該帶給人的感覺(jué)——不是一大鍋黏糊糊的、亂七八糟的東西,而是真正的、富含營(yíng)養(yǎng)的、口味上佳的醬汁。
評(píng)價(jià)設(shè)計(jì)質(zhì)量的最佳方式之一,就是聽(tīng)從直覺(jué)。直覺(jué)不是魔術(shù),它是經(jīng)驗(yàn)和技能的厚積薄發(fā)之產(chǎn)物。在查看一個(gè)設(shè)計(jì)時(shí),聽(tīng)從頭腦中的聲音。如果覺(jué)得什么地方不對(duì),那就好好想想,是哪里出現(xiàn)了問(wèn)題。一個(gè)好的設(shè)計(jì)會(huì)讓人覺(jué)得很舒服。
開(kāi)發(fā)可以工作的、最簡(jiǎn)單的解決方案。除非有不可辯駁的原因,否則不要使用模式、原則和高難度技術(shù)之類的東西。
切身感受
當(dāng)你覺(jué)得所編寫(xiě)的代碼中沒(méi)有一行是多余的,并且仍能交付全部的功能時(shí),這種感覺(jué)就對(duì)了。這樣的代碼容易理解和改正。
平衡的藝術(shù)
□ 代碼幾乎總是可以得到進(jìn)一步精煉,但是到了某個(gè)點(diǎn)之后,再做改進(jìn)就不會(huì)帶來(lái)任何實(shí)質(zhì)性的好處了。這時(shí)開(kāi)發(fā)人員就該停下來(lái),去做其他方面的工作了。
□ 要將目標(biāo)牢記在心:簡(jiǎn)單、可讀性高的代碼。強(qiáng)行讓代碼變得優(yōu)雅與過(guò)早優(yōu)化類似,同樣會(huì)產(chǎn)生惡劣的影響。
□ 當(dāng)然,簡(jiǎn)單的解決方案必須要滿足功能需求。為了簡(jiǎn)單而在功能上妥協(xié),這就是過(guò)分簡(jiǎn)化了。
□ 太過(guò)簡(jiǎn)潔不等于簡(jiǎn)單,那樣無(wú)法達(dá)到溝通的目的。
□ 一個(gè)人認(rèn)為簡(jiǎn)單的東西,可能對(duì)另一個(gè)人就意味著復(fù)雜。
30.編寫(xiě)內(nèi)聚的代碼
“你要編寫(xiě)一些新的代碼,首先要決定的就是把這些代碼放在什么地方。其實(shí)放在什么地方問(wèn)題不大,你就趕緊開(kāi)始吧,看看IDE中現(xiàn)在打開(kāi)的是哪個(gè)類,直接加進(jìn)去就是了。如果所有的代碼都在一個(gè)類或組件里面,要找起來(lái)是很方便的。”
內(nèi)聚性用來(lái)評(píng)估一個(gè)組件(包、模塊或配件)中成員的功能相關(guān)性。內(nèi)聚程度高,表明各個(gè)成員共同完成了一個(gè)功能特性或是一組功能特性。內(nèi)聚程度低的話,表明各個(gè)成員提供的功能是互不相干的。
假定把所有的衣服都扔到一個(gè)抽屜里面。當(dāng)需要找一雙襪子的時(shí)候,要翻遍里面所有的衣服——褲子、內(nèi)衣、T恤等——才能找到。這很麻煩,特別是在趕時(shí)間的時(shí)候。現(xiàn)在,假定把所有的襪子都放在一個(gè)抽屜里面(而且是成雙放置的),全部的T恤放在另外一個(gè)抽屜中,其他衣服也分門別類。要找到一雙襪子,只要打開(kāi)正確的抽屜就可以了。
與此類似,如何組織一個(gè)組件中的代碼,會(huì)對(duì)開(kāi)發(fā)人員的生產(chǎn)力和全部代碼的可維護(hù)性產(chǎn)生重要影響。在決定創(chuàng)建一個(gè)類的時(shí)候,問(wèn)問(wèn)自己,這個(gè)類的功能是不是與組件中其他某個(gè)類的功能類似,而且功能緊密相關(guān)。這就是組件級(jí)的內(nèi)聚性。
類也要遵循內(nèi)聚性。如果一個(gè)類的方法和屬性共同完成了一個(gè)功能(或是一系列緊密相關(guān)的功能),這個(gè)類就是內(nèi)聚的。
看看Charles Hess先生于1866年申請(qǐng)的專利,“可變換的鋼琴、睡椅和五斗柜”(見(jiàn)圖6-2)。
根據(jù)他的專利說(shuō)明,他提供了“……附加的睡椅和五斗柜……以填滿鋼琴下未被使用的空間……”。接下來(lái)他說(shuō)明了為什么要發(fā)明這個(gè)可變換的鋼琴。讀者可能已經(jīng)見(jiàn)過(guò)類似這種發(fā)明的項(xiàng)目代碼結(jié)構(gòu)了,而且也許其中有你的份。這個(gè)發(fā)明不具備任何內(nèi)聚性,任何一個(gè)人都可以想象得到,要維護(hù)這個(gè)怪物(比如換墊子、調(diào)鋼琴等)會(huì)是多么困難。
看看最近的例子。Venkat曾經(jīng)見(jiàn)過(guò)一個(gè)用ASP開(kāi)發(fā)的、有20個(gè)頁(yè)面的Web應(yīng)用。每個(gè)頁(yè)面都以HTML開(kāi)頭,并包含大量VBScript腳本,其中還 了訪問(wèn)數(shù)據(jù)庫(kù)的SQL語(yǔ)句。客戶當(dāng)然會(huì)認(rèn)為這個(gè)應(yīng)用的開(kāi)發(fā)已經(jīng)失去了控制,并且無(wú)法維護(hù)。如果每個(gè)頁(yè)面都包括展示邏輯、業(yè)務(wù)邏輯和訪問(wèn)數(shù)據(jù)看代碼,就有太多的東西都堆在一個(gè)地方了。
假定要對(duì)數(shù)據(jù)庫(kù)的表結(jié)構(gòu)進(jìn)行一次微調(diào)。這個(gè)微小的變化會(huì)導(dǎo)致應(yīng)用中所有的頁(yè)面發(fā)生變化,而且每個(gè)頁(yè)面中都會(huì)有多處改變——這個(gè)應(yīng)用很快就變成了一場(chǎng)災(zāi)難。
如果應(yīng)用使用了中間層對(duì)象(比如一個(gè)COM組件)來(lái)訪問(wèn)數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)表結(jié)構(gòu)更所造成的影響就可以控制在一定的范圍之內(nèi),代碼也更容易維護(hù)。
低內(nèi)聚性的代碼會(huì)造成很嚴(yán)重的后果。假設(shè)有這樣一個(gè)類,實(shí)現(xiàn)了物種完全不想干的功能。如果這5個(gè)功能的需求或希捷發(fā)生了變化,這個(gè)類也必須跟著改變。如果一個(gè)(或者一個(gè)組件)變化的過(guò)于頻繁,這樣的改變會(huì)對(duì)整個(gè)系統(tǒng)形成“漣漪效應(yīng)”,并導(dǎo)致更多的維護(hù)和成本的發(fā)生。考慮另一個(gè)只實(shí)現(xiàn)了一種功能的類,這個(gè)類變化的頻度就沒(méi)有那么高。類似的,一個(gè)更具內(nèi)聚性的組件不會(huì)有太多導(dǎo)致其變化的原因,也因此而更加穩(wěn)定。根據(jù)單一指責(zé)原則(查看《敏捷軟件開(kāi)發(fā):原則、模式與實(shí)踐》[Mar02]),一個(gè)模塊應(yīng)該只有一個(gè)發(fā)生變化的原因。
一些設(shè)計(jì)技巧可以起到幫助作用。舉例來(lái)說(shuō),我們常常使用模型-視圖-控制器(MVC)模式來(lái)分離展示層邏輯、控制器和模型。這個(gè)模式非常有效,因?yàn)樗梢宰岄_(kāi)發(fā)人員獲得更高的內(nèi)聚性。模型中的類包含一種功能,在控制器中的類包含另外的功能,而駛?cè)胫械念悇t只關(guān)心UI。
內(nèi)聚性會(huì)影響一個(gè)組件的可重用性。組件粒度是在設(shè)計(jì)時(shí)要考慮的一個(gè)重要因素。根據(jù)重用發(fā)布等價(jià)原則([Mar02]):重用的粒度于發(fā)布的粒度相同。這就是說(shuō),程序庫(kù)用戶所需要的,是完整的程序庫(kù),而不是其中的一部分。如果不能遵循這個(gè)原則,組件用戶就會(huì)被強(qiáng)迫只能使用所發(fā)布組件的一部分。很不幸的是,他們?nèi)匀粫?huì)被不關(guān)心的那一部分的更新所影響。軟件包越大,可重用性就越差。
讓類的功能盡量集中,讓組件盡量小。要避免創(chuàng)建很大的類或組件,也不要?jiǎng)?chuàng)建無(wú)所不包的大雜燴類。
切身感受
感覺(jué)類和組件的功能都很集中:每個(gè)類或組件只做一件事,而且做得很好。Bug很容易跟蹤,代碼也易于修改,因?yàn)轭惡徒M件的責(zé)任都很清晰。
平衡的藝術(shù)
□ 有可能會(huì)把一些東西拆分成很多微小的部分,而使其失去了實(shí)用價(jià)值。當(dāng)你需要一只襪子的時(shí)候,一盒棉線不能帶給你任何幫助。
□ 具有良好內(nèi)聚性的代碼,可能會(huì)根據(jù)需求的變化,而成比例地進(jìn)行變更。考慮一下,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的功能變化需要變更多少代碼。
31.告知,不要詢問(wèn)
“不要相信其它的對(duì)象。畢竟,它們是有別人寫(xiě)的,甚至有可能是你自己上個(gè)月頭腦發(fā)昏的時(shí)候?qū)懙哪亍膭e人那里去拿你需要的信息,然后自己處理,自己決策。不要放棄控制別人的機(jī)會(huì)!”
“面向過(guò)程的代碼取得信息,然后做出決策。面向?qū)ο蟮拇a讓別的對(duì)象去做事情。”Alec Sharp[Sha97]通過(guò)觀察后,一針見(jiàn)血地指出了這個(gè)關(guān)鍵點(diǎn)。但是這種說(shuō)法并不僅限于面向?qū)ο蟮拈_(kāi)發(fā),任何敏捷的代碼都應(yīng)該遵循這個(gè)方式。
作為某段代碼的調(diào)用者,開(kāi)發(fā)人員絕對(duì)不應(yīng)該基于被調(diào)用對(duì)象的狀態(tài)來(lái)做出任何決策,更不能去改變?cè)搶?duì)象的狀態(tài)。這樣的邏輯應(yīng)該是被調(diào)用對(duì)象的責(zé)任,而不是你的。在該對(duì)象之外替它做決策,就違反了它的封裝原則,而且為bug提供了滋生的土壤。
David Bock使用“送報(bào)男孩和錢包的故事”很好地詮釋了這一點(diǎn)。假定送報(bào)男孩來(lái)到你的門前,要求付給他本周的報(bào)酬。你轉(zhuǎn)過(guò)身去,讓送報(bào)男孩從你的后屁股兜里掏出錢包,并且從中拿走兩美元(你希望是這么多),在把錢包放回去。然后,送報(bào)男孩就會(huì)開(kāi)著他嶄新的美洲豹汽車揚(yáng)長(zhǎng)而去了。在這個(gè)過(guò)程中,送報(bào)男孩作為“調(diào)用者”,應(yīng)該告訴
客戶付他兩美元。他不能探詢客戶的財(cái)務(wù)狀況,或是 將命令與查詢分離開(kāi)來(lái)
錢包的薄厚,他也不能代替客戶做任何決策。這都是 Keep commands separate from queries
客戶的責(zé)任,而不屬于送報(bào)男孩。敏捷代碼也應(yīng)該以
同樣的方式工作。
與告知,不要詢問(wèn)相關(guān)的一個(gè)很有用的技術(shù)是:命令與查詢相分離模式(command-query separation)。就是要將功能和方法分為“命令”和“查詢”兩類,并在源碼中紀(jì)錄下來(lái)(這樣做可以幫助將所有的“命令”代碼放在一起,并將所有的“查詢”代碼放在一起)。
一個(gè)常規(guī)的“命令”可能會(huì)改變對(duì)象的狀態(tài),而且有可能返回一些有用的值,以方便使用.一個(gè)“查詢”僅僅提供給開(kāi)發(fā)人員對(duì)象的狀態(tài),并不會(huì)對(duì)其外部的可見(jiàn)狀態(tài)進(jìn)行修改.
這就是說(shuō),從外部看來(lái),“查詢”不應(yīng)該有任何副作用(如果需要的話,開(kāi)發(fā)人員可能想在后臺(tái)做一些事先的計(jì)算或是緩存處理,但是取得對(duì)象中X的值,不應(yīng)該改變Y的值).
像“命令”這種會(huì)產(chǎn)生內(nèi)部影響的方法,強(qiáng)化了告知,不要詢問(wèn)的建議.此外保證“查詢”沒(méi)有副作用,也是很好的編碼實(shí)踐,因?yàn)殚_(kāi)發(fā)人員可以在單元測(cè)試中自由使它們,在不斷言或者調(diào)試器中調(diào)用它們,而不會(huì)改變應(yīng)用的狀態(tài).
從外部將“查詢”與“命令”隔離開(kāi)來(lái),還會(huì)給開(kāi)發(fā)人員機(jī)會(huì)詢問(wèn)自己為什么要暴露某些特定的數(shù)據(jù).真的需要這么做嗎?調(diào)用者會(huì)如何使用它? 也許應(yīng)該有一個(gè)相關(guān)的“名列”來(lái)替代它.
告知,不要詢問(wèn)。不要搶別的對(duì)象或是組件的工作。告訴它做什么,然后盯著你自己的職責(zé)就好了。
切身感受Smalltalk使用“信息傳遞”的概念,而不是方法調(diào)用。告知,不要詢問(wèn)感覺(jué)起來(lái)就像你的發(fā)送消息,而不是調(diào)用函數(shù)。
平衡的藝術(shù)
□ 一個(gè)對(duì)象,如果只有用作大量數(shù)據(jù)容器,這樣的做法很可疑,有些情況不會(huì)需要這樣的東芝,但并不像想象的那么頻繁。
□ 一個(gè)“命令”返回?cái)?shù)據(jù)以方便使用是沒(méi)有問(wèn)題的(如果需要的話,創(chuàng)建單獨(dú)讀取數(shù)據(jù)的方法也是可以的)。
□ 絕對(duì)不能允許一個(gè)看起來(lái)無(wú)辜的“查詢”去修改對(duì)象的狀態(tài)。
32.根據(jù)契約進(jìn)行替換
“深沉次的繼承是很棒的。如果你需要其他類的函數(shù),直接繼承它們就好了!不要擔(dān)心你創(chuàng)建的新類會(huì)造成破壞,你的調(diào)用者可以改變他們的代碼。這是他們的問(wèn)題,而不是你的問(wèn)題。”
保持系統(tǒng)靈活的關(guān)鍵方式,是當(dāng)新代碼取代原有代碼之后,其他已有的代碼不會(huì)意思到任何差別。例如,某個(gè)開(kāi)發(fā)人員可能想為通信的底層架構(gòu)添加一種新的加密方式,或者使用同樣的接口實(shí)現(xiàn)更好的搜索算法。只要接口保持不變,開(kāi)發(fā)人員就可以隨意修改實(shí)現(xiàn)代碼,而不是影響其他任何現(xiàn)有代碼。然而,說(shuō)起來(lái)容易,做起來(lái)難。所以需要一點(diǎn)指導(dǎo)來(lái)幫助我們正確的實(shí)現(xiàn)。因此,去看看BarbaraLiskove的說(shuō)法。
Liskov替換原則[Lis88]告訴我們:任何繼承之后得到派生類對(duì)象,必須可以替換任何被使用的基類對(duì)象,而且使用者不必知道任何差異。換句話說(shuō),某段代碼如果使用了基類中的方法,就必須能夠使用派生類的對(duì)象,并且自己不必進(jìn)行任何修改。
這到底以為著什么?假定某個(gè)類中有一個(gè)簡(jiǎn)單的方法,用來(lái)對(duì)一個(gè)字符串表進(jìn)行排序,然后返回一個(gè)新的列表。并用如下的方式調(diào)用:
Utils = new BasicUtils();
…
sortedList = utils.sort(aList);
現(xiàn)在假定開(kāi)發(fā)人員派生了一個(gè)BasicUtils的之類,并寫(xiě)了一個(gè)新的sort()方法,使用了更快、更好的排序算法:
Utils = new FasterUtils();
…
sortedList = utils.sort(aList);
注意對(duì)象sort()的調(diào)用是完全一樣的,一個(gè)FasterUtils對(duì)象完美地替換了一個(gè)BasicUtils對(duì)象。調(diào)用utils.sort()的代碼可以處理任何類型的utils對(duì)象而且可以正常工作.
但如果開(kāi)發(fā)人員派生了一個(gè)basicUtils的子類,并改變了排序的意義--也許返回的列表以相反的列表進(jìn)行排列--那就嚴(yán)重違反了Liskov替換原則。
要遵守Liskov替換原則,相對(duì)基類的對(duì)應(yīng)方法,派生類服務(wù)(方法)應(yīng)該不要求更多,不承諾更少;要可以進(jìn)行自由的替換。在設(shè)計(jì)類的繼承層次時(shí),這是一個(gè)非常重要的考慮因素。
繼承是OO建模和編程中被濫用最多的概念之一。如果違反了Lisakov替換原則,繼承層次可能仍然可以提供代碼的可重用性,但是將會(huì)失去可擴(kuò)展性。類繼承關(guān)系的使用者現(xiàn)在必須要檢查給定對(duì)象的類型,以確定如何針對(duì)其進(jìn)行處理。當(dāng)引入了新的類之后,調(diào)用代碼必須經(jīng)常重新評(píng)估并修正。這不是敏捷的方式。
但是可以借用一些幫助。編譯器可以幫助開(kāi)發(fā)人員強(qiáng)制執(zhí)行Liskov 替換原則,至少在某種程度上是可以達(dá)到的。例如,針對(duì)方法的訪問(wèn)修飾符。在java中重寫(xiě)方法的訪問(wèn)修飾符必須與被重寫(xiě)方法的修改符相同,或者可訪問(wèn)范圍更加寬大,也就是說(shuō)如果基類方法是保護(hù)的,那么派生重寫(xiě)方法的修飾符必須是保護(hù)的或者公共的。在C#和VB.NET中,被重寫(xiě)方法與重寫(xiě)方法的訪問(wèn)保護(hù)范圍必須完全相同。
考慮一個(gè)帶有findLargest()方法的類Base,方法中拋出一個(gè)IndexOut-OfRangeException異常。基于文檔,類的使用者會(huì)準(zhǔn)備抓住可能被拋出的異常。現(xiàn)在,假定你從Base類繼承得到類Derived,并重寫(xiě)了findLargest()方法,在新的方法中拋出了一個(gè)不同的異常。現(xiàn)在如果某段代碼期待使用Base類對(duì)象,并調(diào)用了Derived類的實(shí)例,這段代碼就有可能接受到一個(gè)意想不到的異常。你的Derived類就不能替換使用到Base類的地方。在java中,通過(guò)不允許重寫(xiě)方法拋出任何新的檢查異常避免了這個(gè)問(wèn)題,除非異常本身派生自被重寫(xiě)方法拋出的異常類(當(dāng)然,對(duì)于像RuntimeException這樣的未檢查異常,編譯器就不能幫你了)。
不幸的是,java也違背了Liskov替換原則。Java.util.Stack類派生自java.util.Vector類。如果開(kāi)發(fā)人員(不小心)將Stack對(duì)象發(fā)送給一個(gè)期待Vector實(shí)例的方法,Stack中的元素就可能被以與期望的行為不符的順序被插入活刪除。
當(dāng)使用繼承時(shí),要想想派生類是否可以替換基類。如果答案是
不能,就要問(wèn)問(wèn)自己為什么要使用繼承。如果答案是希望在編譯寫(xiě)新類的時(shí)候,還要重用基類的代碼,也許要考慮轉(zhuǎn)而使用聚合,聚合是指在類中包含一個(gè)對(duì)象,并且該對(duì)象是其他類的實(shí)例,開(kāi)發(fā)人員將責(zé)任委托給所包含的對(duì)象來(lái)完成(該技術(shù)同樣被稱為委托)。
圖6-3中展示了委托與繼承之間的差異。在圖中,一個(gè)調(diào)用者調(diào)用了CalledClass中的MethodA(),而它將會(huì)通過(guò)繼承直接調(diào)用Base Class中的說(shuō)法。在委托的模型中,Called Class必須要顯式地將方法調(diào)用轉(zhuǎn)向包含的委托方法。
Base Class
methodA()
Dekegate ClassmethodA()
Called Class
methodA()à1Called Class繼承 委托圖6-3 委托與繼承
那么繼承和委托分別在什么時(shí)候使用呢?
□ 如果新類可以替換已有的類,并且它們之間的關(guān)系可以通過(guò)is-a來(lái)描述,就要使用繼承
□ 如果新類只是使用已有的類,并且二者之間的關(guān)系可以描述為has-a或者user-a就使用委托吧。
開(kāi)發(fā)人員可能會(huì)爭(zhēng)辯說(shuō),在使用委托時(shí),必須要寫(xiě)很多小方法,來(lái)將方法調(diào)用指向所包含的對(duì)象。在繼承中,不需要這樣做,因?yàn)榛愔械墓卜椒ㄔ谂缮愔芯鸵呀?jīng)是可用的了,僅憑這一點(diǎn),并不能構(gòu)成使用繼承足夠好的理由。
你可以在開(kāi)發(fā)一個(gè)好的腳本或是好的IDE宏,來(lái)幫助編寫(xiě)這幾行代碼,或者使用一種更好的編程語(yǔ)言/環(huán)境,以支持更自動(dòng)化形式的委托(比如Ruby這一點(diǎn)就做的不錯(cuò)了)。
通過(guò)替換代碼來(lái)擴(kuò)展系統(tǒng)。通過(guò)替換遵循接口契約的來(lái),來(lái)添加并改進(jìn)功能特性。要多使用委托而不是繼承。
切身感受
這會(huì)讓人覺(jué)得有點(diǎn)鬼鬼祟祟的,你可以偷偷地替換組件代碼到代碼庫(kù)中,而且其他代碼對(duì)比此一無(wú)所知,它們還獲得了新的或改進(jìn)后的功能。
平衡藝術(shù)
□ 相對(duì)繼承來(lái)說(shuō),委托更加靈活,適應(yīng)力也更強(qiáng)。
□ 繼承不是魔鬼,只是長(zhǎng)久以來(lái)被大家誤解了。
□ 如果你不確定一個(gè)接口做出了什么樣的承諾,或是有什么樣的需求,那就很難提供一個(gè)對(duì)其有意義的實(shí)現(xiàn)了。
第7章、敏捷調(diào)試
你也許會(huì)對(duì)木匠那毫無(wú)差錯(cuò)的工作印象深刻,但我向你保證,事實(shí)不是這樣的,真正的高手只是知道如何亡羊補(bǔ)牢。--Jeff Miller,家具制造者,作家
即使是運(yùn)作得最后的敏捷項(xiàng)目,也會(huì)發(fā)生錯(cuò)誤。Bug、錯(cuò)、缺陷—不管被稱作什么,它們總會(huì)發(fā)生。
在調(diào)試時(shí)面對(duì)的真正問(wèn)題,是無(wú)法用固定的時(shí)間來(lái)限制。可以規(guī)定設(shè)計(jì)會(huì)議的持續(xù)時(shí)間,并在時(shí)間截止時(shí)采用最佳的方案。但是調(diào)試所耗費(fèi)的時(shí)間,可能是一個(gè)小時(shí)、一天,甚至一周過(guò)去了,還是沒(méi)有辦法找到并解決問(wèn)題。
對(duì)于一個(gè)項(xiàng)目來(lái)說(shuō),這樣沒(méi)有準(zhǔn)備把握的時(shí)間消耗是不可接受。不過(guò),我們可以使用一些輔助技術(shù),涵蓋的范圍包括:保留以前的問(wèn)題解決方案,以及提供發(fā)生問(wèn)題時(shí)的更多有用細(xì)節(jié)。
想要更加有效的重用你的知識(shí)和努力,記錄問(wèn)題解決日志是很有用的,我們會(huì)在下一頁(yè)看到如何具體操作。當(dāng)編譯器警告有問(wèn)題的時(shí)候,要假定警告就是錯(cuò)誤,并且馬上把它們解決掉(第132頁(yè))。
想在一個(gè)完整的系統(tǒng)中跟蹤問(wèn)題非常困難—甚至是不可能的。如果可以對(duì)問(wèn)題各個(gè)擊破,正如我們?cè)诘?36頁(yè)中看到的那樣,就更容易找到問(wèn)題了,不同與某些欲蓋彌彰的行為,應(yīng)報(bào)告所有的異常,如第139頁(yè)所述。最后,在報(bào)告某些事情出錯(cuò)之時(shí),必須要考慮用戶的感受,并且提供有用的錯(cuò)誤信息。我們會(huì)在第141頁(yè)看到這是為什么。
33 記錄問(wèn)題解決日志“在開(kāi)發(fā)過(guò)程中是不是經(jīng)常遇到似曾相識(shí)的問(wèn)題?這沒(méi)關(guān)系。以前解決過(guò)的問(wèn)題,現(xiàn)在還是可以解決掉的。”
面對(duì)問(wèn)題(并解決它們)是開(kāi)發(fā)人員的一種生活方式。當(dāng)問(wèn)題發(fā)生時(shí),我們希望趕緊把它解決掉。如果一個(gè)熟悉的問(wèn)題再次發(fā)生,我們會(huì)希望記起第一次是如何解決的,而且下午下次能夠更快地把它搞定。然而,有時(shí)一個(gè)問(wèn)題看起來(lái)跟以前遇到的完全一樣,但是我們卻不記得是如何修復(fù)的了。這種狀況時(shí)常發(fā)生。
不能通過(guò)Wed搜索獲得答案嗎?畢竟互聯(lián)網(wǎng)已經(jīng)成長(zhǎng)為如此令人難以置信的信息來(lái)源,我們也應(yīng)該好好加以利用。從Wed上尋找答案當(dāng)然勝過(guò)僅靠個(gè)人努力解決問(wèn)題。可這是非常耗費(fèi)時(shí)間的過(guò)程。有時(shí)可以找到需要的答案,有時(shí)除了找到一大堆意見(jiàn)和建議之外,發(fā)現(xiàn)不了實(shí)質(zhì)性的解決方案。看到有多少開(kāi)發(fā)人員遇到同樣的問(wèn)題,也許會(huì)感覺(jué)不錯(cuò),但我們需要的是一個(gè)解決辦法。
想要得到更好的效果,不妨維護(hù)一個(gè)保存曾遇到的問(wèn)題以及對(duì)應(yīng)解決方案的日志。這樣,當(dāng)問(wèn)題發(fā)生時(shí),就不必說(shuō):“嘿,我曾碰到過(guò)這個(gè)問(wèn)題,
但是不記得是怎么解決的了。”可以快速搜索以前用 不要在同一處跌倒兩次
過(guò)的方法。工程師們已經(jīng)使用這種方式很多年來(lái), Don’t get burned twice
他們稱之為每日日志(daylog)可以選擇符合要求的任何格式。下面這些條目可能會(huì)用得上。
□ 問(wèn)題發(fā)生日期。
□ 問(wèn)題簡(jiǎn)述。
□ 解決方案詳細(xì)描述。
□ 引用文章后網(wǎng)址,以提供更多細(xì)節(jié)或相關(guān)信息。
□ 任何代碼片段、設(shè)置后對(duì)話框的截屏,只要它們是解決方案的一部分,或者可以幫助更深入地理解相關(guān)細(xì)節(jié)。
要將日志保存為可供計(jì)算機(jī)搜索的格式,就可以進(jìn)行關(guān)鍵字搜索以快速查找細(xì)節(jié)。圖7-1展示了一個(gè)簡(jiǎn)單的例子,其中帶有超鏈接以提供更多信息。圖7-1 帶有超鏈接的解決方案條目示例
如果面臨的問(wèn)題無(wú)法在日志中找到解決方案,在問(wèn)題解決之后,要記得馬上將新的細(xì)節(jié)記錄到日志中去。
要共享日志給其他人,而不僅僅是靠一個(gè)維護(hù)。把它放到共享的網(wǎng)絡(luò)驅(qū)動(dòng)器中,這樣其他人也可以使用。或者創(chuàng)建一個(gè)Wiki,并鼓勵(lì)其他開(kāi)發(fā)人員使用和更新其內(nèi)容。
維護(hù)一個(gè)問(wèn)題及其解決方案的日志。保留解決方案是修復(fù)問(wèn)題過(guò)程的一部分,以后發(fā)生相同或類似問(wèn)題時(shí),就可以很快找到并使用了。
切實(shí)感受
解決方案日志應(yīng)該作為思考的一個(gè)來(lái)源,可以在其中發(fā)現(xiàn)某些特定問(wèn)題的細(xì)節(jié),對(duì)于某些類似但是有差異的問(wèn)題,也能從中獲得修復(fù)的指引。
平衡的藝術(shù)□ 記錄問(wèn)題的時(shí)間不能超過(guò)在解決問(wèn)題上花費(fèi)的時(shí)間。要保持輕量級(jí)和簡(jiǎn)單,不必達(dá)到對(duì)外發(fā)布式的質(zhì)量。□ 找到以前的解決方法非常關(guān)鍵。使用足夠的關(guān)鍵字,可以幫助你在需要的時(shí)候發(fā)現(xiàn)需要的條目。□ 如果通過(guò)搜索Wed,發(fā)現(xiàn)沒(méi)人曾經(jīng)遇到同樣的問(wèn)題,也許搜索的方式有問(wèn)題。□ 要記錄發(fā)生問(wèn)題時(shí)應(yīng)用程序、應(yīng)用框架或平臺(tái)的特定版本。同樣的問(wèn)題在不同的平臺(tái)或版本上可能表現(xiàn)得不同。
□ 要記錄團(tuán)隊(duì)做出一個(gè)重要決策的原因。否則,在6~9個(gè)月之后,想在重新回顧決策過(guò)程的時(shí)候,這些細(xì)節(jié)就很難在記得了,很容易發(fā)生互相指責(zé)的情形。
34 警告就是錯(cuò)誤
“編譯器的警告信息只不過(guò)是給過(guò)分小心和過(guò)于書(shū)呆子氣的人看的。他們只是警告而已。如果導(dǎo)致的后果很嚴(yán)重,它們就是錯(cuò)誤了,而且會(huì)導(dǎo)致無(wú)法通過(guò)編譯,所以干脆忽略它們就是了。”
當(dāng)程序中出現(xiàn)一個(gè)編譯錯(cuò)誤時(shí),編譯器或是構(gòu)建工具會(huì)拒絕產(chǎn)生可執(zhí)行文件,我們別無(wú)選擇——必須要先修正錯(cuò)誤,在繼續(xù)前行。
然而,警告卻是另外一種狀況。即使代碼編譯時(shí)產(chǎn)生了警告,我們還是可以運(yùn)行程序。那么忽略警告信息繼續(xù)開(kāi)發(fā)代碼,會(huì)導(dǎo)致什么狀況呢?這樣做等于是坐在了一個(gè)嘀嗒作響的定時(shí)炸彈上,而且它很有可能在最糟糕的時(shí)刻爆炸。
有些警告是過(guò)于挑剔的編譯器的良性副產(chǎn)品,有些則不是。例如:一個(gè)關(guān)于未被使用的變量的警告,可能不會(huì)產(chǎn)生什么惡劣影響,但卻有可能是暗示某些變量被錯(cuò)誤使用了。
最近在一家客戶那里,Venkat發(fā)現(xiàn)一個(gè)開(kāi)發(fā)中的應(yīng)用有多于300個(gè)警告。其中一個(gè)被開(kāi)發(fā)人員忽略的警告是這樣:
Assignment in conditional expression is always constant;
Did you mean to use == instead of = ?
條件的表達(dá)式中賦值為常量,你是否要使用==而不是=?
相關(guān)的代碼如下:
If (theTextBox.Visible = true)…
也就是說(shuō),If語(yǔ)句總是會(huì)評(píng)估為true,無(wú)論不幸的theTextBox變量是什么狀況。看到類似這樣真正的錯(cuò)誤被當(dāng)作警告忽略掉,真是令人感到害怕。
看看下面的C#代碼:
Public class Base{Public virtual void foo(){
Console.WriteLine(“Base.foo”);}}
Public class Derived :Base{Public vitrual viod foo(){Console.WriteLine(“Derived.foo”);}}Class Test{Static void Main(string[] args){Derived d = new Derived();Base b = d;d.foo();b.foo();}}在使用Visual Studio 2003默認(rèn)的項(xiàng)目設(shè)置對(duì)其進(jìn)行編譯時(shí),會(huì)看到如此信息“構(gòu)建 1個(gè)成功,0失敗,0跳過(guò)”顯示在Output窗口的底部。運(yùn)行程序,會(huì)得到這樣的輸出:
Derived.foo
Base.foo
但這不是我們預(yù)期的結(jié)果。應(yīng)該看到兩次對(duì)Derived類中foo方法的調(diào)用。是哪里出錯(cuò)了?如果仔細(xì)查看Ouput窗口,可以發(fā)現(xiàn)這樣的警告信息:
Warning.Derived.foo hides inherited member Base.foo
To make the current member overrode that implementation,
Add the override keyword.Otherwise 、 you’ d add the new keyword.
這明顯是一個(gè)錯(cuò)誤——在Derived類的foo()方法中,應(yīng)該使用override而不是virtual。 想象一下,有組織地忽略代碼中類似這樣的錯(cuò)誤會(huì)導(dǎo)致什么樣的后果。代碼的行為會(huì)變得無(wú)法預(yù)測(cè),其質(zhì)量會(huì)直線下降。
可能有人會(huì)說(shuō)優(yōu)秀的單元測(cè)試可以發(fā)現(xiàn)這些問(wèn)題。是的,它們可以起到幫助作用(而且也應(yīng)該使用優(yōu)秀的單元測(cè)試)。可如果編譯器可以發(fā)現(xiàn)這種問(wèn)題,那為什么不利用它呢?
這可以節(jié)省大量的時(shí)間和麻煩。
要找到一種方式讓編譯器將警告信息作為錯(cuò)誤提示出來(lái)。如果編譯器允許調(diào)整警告的報(bào)告級(jí)別,那就把級(jí)別調(diào)到最高,讓任何警告不能被忽略。例如:GCC編譯器支持-Werror參數(shù),在Visual Studio中,開(kāi)發(fā)人員可以改變項(xiàng)目的設(shè)置,將警告視為錯(cuò)誤。
對(duì)于一個(gè)項(xiàng)目的警告信息來(lái)說(shuō),至少也要做到這種地步。然而,如果采取這種方式,就要?jiǎng)?chuàng)建每個(gè)項(xiàng)目去進(jìn)行設(shè)置。如果可以盡量以全局化的方式來(lái)進(jìn)行設(shè)置就好了。
比如,在Visual Studio中,開(kāi)發(fā)人員可以修改項(xiàng)目模板(查看.NET Gotchas[Sub05]獲取更多的細(xì)節(jié)),這樣在計(jì)算機(jī)上創(chuàng)建的任何項(xiàng)目,都會(huì)有同樣的完整項(xiàng)目設(shè)置。在當(dāng)前版本Eclipse中,可以按照這樣的順序修改設(shè)置:Windows—Preferences-java-Compiler-Errors/Warnings。如果使用其他的語(yǔ)言或IDE,花一些時(shí)間來(lái)找出如何在其中將警告作為錯(cuò)誤處理吧。
在修改設(shè)置的時(shí)候,要記得在構(gòu)建服務(wù)器上使用的持續(xù)集成工具中,修改同樣的設(shè)置選項(xiàng)。(要詳細(xì)的了解持續(xù)集成,查看第87頁(yè)習(xí)慣21。)這個(gè)小小的設(shè)置,可以提升團(tuán)隊(duì)簽入到源碼控制系統(tǒng)中的代碼質(zhì)量。
在開(kāi)始一個(gè)項(xiàng)目的時(shí)候,要把相關(guān)的設(shè)置都準(zhǔn)備好。在項(xiàng)目進(jìn)行到一半的時(shí)候,突然改變警告設(shè)置,有可能會(huì)帶來(lái)顛覆性的后果,導(dǎo)致難以控制。
編譯器可以輕易處理警告信息,可是你不能。
警告給人的感覺(jué)就像……哦,警告。他們就某些問(wèn)題給出警告,來(lái)吸引開(kāi)發(fā)人員的注意。
平衡的藝術(shù)
□ 雖然這里探討的主要是編譯語(yǔ)言,解釋型語(yǔ)言通常也有標(biāo)志,允許運(yùn)行時(shí)警告。使用相關(guān)標(biāo)志,然后捕獲輸出,以識(shí)別并最終消除警告。
□ 由于編譯器的bug或第三方工具或代碼的原因,有些警告無(wú)法消除。如果確實(shí)沒(méi)有應(yīng)對(duì)之策的話,就不要在浪費(fèi)更多的時(shí)間了。但是類似的狀況很少發(fā)生。
□ 應(yīng)該經(jīng)常指示編譯器:要特別注意別將無(wú)法避免的警告作為錯(cuò)誤進(jìn)行提示,這樣就不用費(fèi)力去查看所有的提示,以找到真正的錯(cuò)誤和警告。
□ 棄用的方法被棄用是有原因的。不要再使用它們了。至少,安排一個(gè)迭代器來(lái)將它們(以及它們引起的警告信息)安全地移除掉。
□ 如果將過(guò)去開(kāi)發(fā)完成的方法標(biāo)記為棄用方法,要記錄當(dāng)前用戶應(yīng)該采取何種變通之策,以及被棄用的方法將會(huì)在何時(shí)一起移除
?
總結(jié)
以上是生活随笔為你收集整理的高效程序员的45个习惯的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 西北工业大学计算机考研资料汇总
- 下一篇: 基于HTML+CSS+JavaScrip