




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
程序員修煉之道1我的源碼讓貓給吃了 32軟件的熵 53石頭湯與煮青蛙 74足夠好的軟件 95你的知識(shí)資產(chǎn) 116交流! 167重復(fù)的危害 218正交性 299可撤消性 3710曳光彈 4011原型與便箋 4412領(lǐng)域語言 4813估算 5414純文本的威力 5915shell游戲 6416強(qiáng)力編輯 6817源碼控制 7118調(diào)試 7419文本操縱 8120代碼生成器 8421按合約設(shè)計(jì)(1) 8721按合約設(shè)計(jì)(2) 9222死程序不說謊 9923斷言式編程 10124何時(shí)使用異常 10425怎樣配平資源 1081我的源碼讓貓給吃了注重實(shí)效的程序員的特征是什么?我們覺得是他們處理問題、尋求解決方案時(shí)的態(tài)度、風(fēng)格、哲學(xué)。他們能夠越出直接的問題去思考,總是設(shè)法把問題放在更大的語境中,總是設(shè)法注意更大的圖景。畢竟,沒有這樣的更大的語境,你又怎能注重實(shí)效?你又怎能做出明智的妥協(xié)和有見識(shí)的決策?他們成功的另一關(guān)鍵是他們對(duì)他們所做的每件事情負(fù)責(zé),關(guān)于這一點(diǎn),我們將在“我的源碼讓貓給吃了”中加以討論。因?yàn)樨?fù)責(zé),注重實(shí)效的程序員不會(huì)坐視他們的項(xiàng)目土崩瓦解。在“軟件的熵”中,我們將告訴你怎樣使你的項(xiàng)目保持整潔。大多數(shù)人發(fā)現(xiàn)自己很難接受變化,有時(shí)是出于好的理由,有時(shí)只是因?yàn)楣逃械亩栊浴T凇笆^湯與煮青蛙”中,我們將考察一種促成變化的策略,并(出于對(duì)平衡的興趣)講述一個(gè)忽視漸變危險(xiǎn)的兩棲動(dòng)物的警世傳說。理解你的工作的語境的好處之一是,了解你的軟件必須有多好變得更容易了。有時(shí)接近完美是惟一的選擇,但常常會(huì)涉及各種權(quán)衡。我們將在“足夠好的軟件”中探究這一問題。當(dāng)然,你需要擁有廣泛的知識(shí)和經(jīng)驗(yàn)基礎(chǔ)才能贏得這一切。學(xué)習(xí)是一個(gè)持續(xù)不斷的過程。在“你的知識(shí)資產(chǎn)”中,我們將討論一些策略,讓你“開足馬力”。最后,我們沒有人生活在真空中。我們都要花大量時(shí)間與他人打交道。在“交流!”中列出了能讓我們更好地做到這一點(diǎn)的幾種途徑。注重實(shí)效的編程源于注重實(shí)效的思考的哲學(xué)。本章將為這種哲學(xué)設(shè)立基礎(chǔ)。1我的源碼讓貓給吃了在所有弱點(diǎn)中,最大的弱點(diǎn)就是害怕暴露弱點(diǎn)?!狫.B.Bossuet,PoliticsfromHolyWrit,1709依據(jù)你的職業(yè)發(fā)展、你的項(xiàng)目和你每天的工作,為你自己和你的行為負(fù)責(zé)這樣一種觀念,是注重實(shí)效的哲學(xué)的一塊基石。注重實(shí)效的程序員對(duì)他或她自己的職業(yè)生涯負(fù)責(zé),并且不害怕承認(rèn)無知或錯(cuò)誤。這肯定并非是編程最令人愉悅的方面,但它肯定會(huì)發(fā)生——即使是在最好的項(xiàng)目中。盡管有徹底的測試、良好的文檔以及足夠的自動(dòng)化,事情還是會(huì)出錯(cuò)。交付晚了,出現(xiàn)了未曾預(yù)見到的技術(shù)問題。發(fā)生這樣的事情,我們要設(shè)法盡可能職業(yè)地處理它們。這意味著誠實(shí)和坦率。我們可以為我們的能力自豪,但對(duì)于我們的缺點(diǎn)——還有我們的無知和我們的錯(cuò)誤——我們必須誠實(shí)。負(fù)責(zé)責(zé)任是你主動(dòng)擔(dān)負(fù)的東西。你承諾確保某件事情正確完成,但你不一定能直接控制事情的每一個(gè)方面。除了盡你所能以外,你必須分析風(fēng)險(xiǎn)是否超出了你的控制。對(duì)于不可能做到的事情或是風(fēng)險(xiǎn)太大的事情,你有權(quán)不去為之負(fù)責(zé)。你必須基于你自己的道德準(zhǔn)則和判斷來做出決定。如果你確實(shí)同意要為某個(gè)結(jié)果負(fù)責(zé),你就應(yīng)切實(shí)負(fù)起責(zé)任。當(dāng)你犯錯(cuò)誤(就如同我們所有人都會(huì)犯錯(cuò)誤一樣)、或是判斷失誤時(shí),誠實(shí)地承認(rèn)它,并設(shè)法給出各種選擇。不要責(zé)備別人或別的東西,或是拼湊借口。不要把所有問題都?xì)w咎于供應(yīng)商、編程語言、管理部門、或是你的同事。也許他(它)們?nèi)w或是某幾方在其中扮演了某種角色,但你可以選擇提供解決方案,而非尋找借口。如果存在供應(yīng)商不能按時(shí)供貨的風(fēng)險(xiǎn),你應(yīng)該預(yù)先制定一份應(yīng)急計(jì)劃。如果磁盤垮了——帶走了你的所有源碼——而你沒有做備份,那是你的錯(cuò)。告訴你的老板“我的源碼讓貓給吃了”也無法改變這一點(diǎn)。提示3ProvideOptions,Don’tMakeLameExcuses提供各種選擇,不要找蹩腳的借口在你走向任何人、告訴他們?yōu)楹文呈伦霾坏?、為何耽擱、為何出問題之前,先停下來,聽一聽你心里的聲音。與你的顯示器上的橡皮鴨交談,或是與貓交談。你的辯解聽起來合理,還是愚蠢?在你老板聽來又是怎樣?在你的頭腦里把談話預(yù)演一遍。其他人可能會(huì)說什么?他們是否會(huì)問:“你試了這個(gè)嗎……”,或是“你沒有考慮那個(gè)嗎?”你將怎樣回答?在你去告訴他們壞消息之前,是否還有其他你可以再試一試的辦法?有時(shí),你其實(shí)知道他們會(huì)說什么,所以還是不要給他們添麻煩吧。要提供各種選擇,而不是找借口。不要說事情做不到;要說明能夠做什么來挽回局面。必須把代碼扔掉?給他們講授重構(gòu)的價(jià)值(參見重構(gòu),184頁)。你要花時(shí)間建立原型(prototyping),以確定最好的繼續(xù)前進(jìn)的方式(參見原型與便箋,53頁)?你要引入更好的測試(參見易于測試的代碼,189頁;以及無情的測試,237頁)或自動(dòng)化(參見無處不在的自動(dòng)化,230頁),以防止問題再度發(fā)生?又或許你需要額外的資源。不要害怕提出要求,也不要害怕承認(rèn)你需要幫助。在你大聲說出它們之前,先設(shè)法把蹩腳的借口清除出去。如果你必須說,就先對(duì)你的貓說。反正,如果小蒂德爾絲(Tiddles,BBC在1969~1974年播出的喜劇節(jié)目“MontyPython'sFlyingCircus”中的著名小母貓——譯注)要承受指責(zé)……2軟件的熵盡管軟件開發(fā)幾乎不受任何物理定律的約束,熵(entropy)對(duì)我們的影響卻很大。熵是一個(gè)來自物理學(xué)的概念,指的是某個(gè)系統(tǒng)中的“無序”的總量。遺憾的是,熱力學(xué)定律保證了宇宙中的熵傾向于最大化。當(dāng)軟件中的無序增長時(shí),程序員們稱之為“軟件腐爛”(softwarerot)。有許多因素可以促生軟件腐爛。其中最重要的一個(gè)似乎是開發(fā)項(xiàng)目時(shí)的心理(或文化)。即使你的團(tuán)隊(duì)只有你一個(gè)人,你開發(fā)項(xiàng)目時(shí)的心理也可能是非常微妙的事情。盡管制定了最好的計(jì)劃,擁有最好的開發(fā)者,項(xiàng)目在其生命期中仍可能遭遇毀滅和衰敗。而另外有一些項(xiàng)目,盡管遇到巨大的困難和接連而來的挫折,卻成功地?fù)魯∽匀坏臒o序傾向,設(shè)法取得了相當(dāng)好的結(jié)果。是什么造成了這樣的差異?在市區(qū),有些建筑漂亮而整潔,而另一些卻是破敗不堪的“廢棄船只”。為什么?犯罪和城市衰退領(lǐng)域的研究者發(fā)現(xiàn)了一種迷人的觸發(fā)機(jī)制,一種能夠很快將整潔、完整和有人居住的建筑變?yōu)槠茢〉膹U棄物的機(jī)制[WK82]。破窗戶。一扇破窗戶,只要有那么一段時(shí)間不修理,就會(huì)漸漸給建筑的居民帶來一種廢棄感——一種職權(quán)部門不關(guān)心這座建筑的感覺。于是又一扇窗戶破了。人們開始亂扔垃圾。出現(xiàn)了亂涂亂畫。嚴(yán)重的結(jié)構(gòu)損壞開始了。在相對(duì)較短的一段時(shí)間里,建筑就被損毀得超出了業(yè)主愿意修理的程度,而廢棄感變成了現(xiàn)實(shí)?!捌拼皯衾碚摗眴l(fā)了紐約和其他大城市的警察部門,他們對(duì)一些輕微的案件嚴(yán)加處理,以防止大案的發(fā)生。這起了作用:管束破窗戶、亂涂亂畫和其他輕微違法事件減少了嚴(yán)重罪案的發(fā)生。提示4Don’tLivewithBrokenWindows不要容忍破窗戶不要留著“破窗戶”(低劣的設(shè)計(jì)、錯(cuò)誤決策、或是糟糕的代碼)不修。發(fā)現(xiàn)一個(gè)就修一個(gè)。如果沒有足夠的時(shí)間進(jìn)行適當(dāng)?shù)男蘩?,就用木板把它釘起來?;蛟S你可以把出問題的代碼放入注釋(commentout),或是顯示“未實(shí)現(xiàn)”消息,或是用虛設(shè)的數(shù)據(jù)(dummydata)加以替代。采取某種行動(dòng)防止進(jìn)一步的損壞,并說明情勢處在你的控制之下。我們看到過整潔、運(yùn)行良好的系統(tǒng),一旦窗戶開始破裂,就相當(dāng)迅速地惡化。還有其他一些因素能夠促生軟件腐爛,我們將在別處探討它們,但與其他任何因素相比,置之不理都會(huì)更快地加速腐爛的進(jìn)程。你也許在想,沒有人有時(shí)間到處清理項(xiàng)目的所有碎玻璃。如果你繼續(xù)這么想,你就最好計(jì)劃找一個(gè)大型垃圾罐,或是搬到別處去。不要讓熵贏得勝利。滅火作為對(duì)照,讓我們講述Andy的一個(gè)熟人的故事。他是一個(gè)富得讓人討厭的富翁,擁有一所完美、漂亮的房子,里面滿是無價(jià)的古董、藝術(shù)品,以及諸如此類的東西。有一天,一幅掛毯掛得離他的臥室壁爐太近了一點(diǎn),著了火。消防人員沖進(jìn)來救火——和他的房子。但他們拖著粗大、骯臟的消防水管沖到房間門口卻停住了——火在咆哮——他們要在前門和著火處之間鋪上墊子。他們不想弄臟地毯。這的確是一個(gè)極端的事例,但我們必須以這樣的方式對(duì)待軟件。一扇破窗戶——一段設(shè)計(jì)低劣的代碼、團(tuán)隊(duì)必須在整個(gè)項(xiàng)目開發(fā)過程中加以忍受的一項(xiàng)糟糕的管理決策——就足以使項(xiàng)目開始衰敗。如果你發(fā)現(xiàn)自己在有好些破窗戶的項(xiàng)目里工作,會(huì)很容易產(chǎn)生這樣的想法:“這些代碼的其余部分也是垃圾,我只要照著做就行了?!表?xiàng)目在這之前是否一直很好,并沒有什么關(guān)系。在最初得出“破窗戶理論”的一項(xiàng)實(shí)驗(yàn)中,一輛廢棄的轎車放了一個(gè)星期,無人理睬。而一旦有一扇窗戶被打破,數(shù)小時(shí)之內(nèi)車上的設(shè)備就被搶奪一空,車也被翻了個(gè)底朝天。按照同樣的道理,如果你發(fā)現(xiàn)你所在團(tuán)隊(duì)和項(xiàng)目的代碼十分漂亮——編寫整潔、設(shè)計(jì)良好,并且很優(yōu)雅——你就很可能會(huì)格外注意不去把它弄臟,就和那些消防員一樣。即使有火在咆哮(最后期限、發(fā)布日期、會(huì)展演示,等等),你也不會(huì)想成為第一個(gè)弄臟東西的人。相關(guān)內(nèi)容:l石頭湯與煮青蛙,7頁l重構(gòu),184頁l注重實(shí)效的團(tuán)隊(duì),224頁挑戰(zhàn)l通過調(diào)查你周邊的計(jì)算“環(huán)境”,幫助增強(qiáng)你的團(tuán)隊(duì)的能力。選擇兩或三扇“破窗戶”,并與你的同事討論問題何在,以及怎樣修理它們。l你能否說出某扇窗戶是何時(shí)破的?你的反應(yīng)是什么?如果它是他人的決策所致,或者是管理部門的指示,你能做些什么?3石頭湯與煮青蛙三個(gè)士兵從戰(zhàn)場返回家鄉(xiāng),在路上餓了。他們看見前面有村莊,就來了精神——他們相信村民會(huì)給他們一頓飯吃。但當(dāng)他們到達(dá)那里,卻發(fā)現(xiàn)門鎖著,窗戶也關(guān)著。經(jīng)歷了多年戰(zhàn)亂,村民們糧食匱乏,并把他們有的一點(diǎn)糧食藏了起來。士兵們并未氣餒,他們煮開一鍋水,小心地把三塊石頭放進(jìn)去。吃驚的村民們走出來望著他們?!斑@是石頭湯?!笔勘鴤兘忉屨f。“就放石頭嗎?”村民們問?!耙稽c(diǎn)沒錯(cuò)——但有人說加一些胡蘿卜味道更好……”一個(gè)村民跑開了,又很快帶著他儲(chǔ)藏的一籃胡蘿卜跑回來。幾分鐘之后,村民們又問:“就是這些了嗎?”“哦,”士兵們說:“幾個(gè)土豆會(huì)讓湯更實(shí)在?!庇忠粋€(gè)村民跑開了。接下來的一小時(shí),士兵們列舉了更多讓湯更鮮美的配料:牛肉、韭菜、鹽,還有香菜。每次都會(huì)有一個(gè)不同的村民跑回去搜尋自己的私人儲(chǔ)藏品。最后他們煮出了一大鍋熱氣騰騰的湯。士兵們拿掉石頭,和所有村民一起享用了一頓美餐,這是幾個(gè)月以來他們所有人第一次吃飽飯。在石頭湯的故事里有兩層寓意。士兵戲弄了村民,他們利用村民的好奇,從他們那里弄到了食物。但更重要的是,士兵充當(dāng)催化劑,把村民團(tuán)結(jié)起來,和他們一起做到了他們自己本來做不到的事情——一項(xiàng)協(xié)作的成果。最后每個(gè)人都是贏家。你常常也可以效仿這些士兵。在有些情況下,你也許確切地知道需要做什么,以及怎樣去做。整個(gè)系統(tǒng)就在你的眼前——你知道它是對(duì)的。但請(qǐng)求許可去處理整個(gè)事情,你會(huì)遇到拖延和漠然。大家要設(shè)立委員會(huì),預(yù)算需要批準(zhǔn),事情會(huì)變得復(fù)雜化。每個(gè)人都會(huì)護(hù)衛(wèi)他們自己的資源。有時(shí)候,這叫做“啟動(dòng)雜役”(start-upfatigue)。這正是拿出石頭的時(shí)候。設(shè)計(jì)出你可以合理要求的東西,好好開發(fā)它。一旦完成,就拿給大家看,讓他們大吃一驚。然后說:“要是我們?cè)黾印赡芫蜁?huì)更好?!奔傺b那并不重要。坐回椅子上,等著他們開始要你增加你本來就想要的功能。人們發(fā)現(xiàn),參與正在發(fā)生的成功要更容易。讓他們瞥見未來,你就能讓他們聚集在你周圍[1]。提示5BeaCatalystforChange做變化的催化劑村民的角度另一方面,石頭湯的故事也是關(guān)于溫和而漸進(jìn)的欺騙的故事。它講述的是過于集中的注意力。村民想著石頭,忘了世界的其余部分。我們都是這樣,每一天。事情會(huì)悄悄爬到我們身上。我們都看見過這樣的癥狀。項(xiàng)目慢慢地、不可改變地完全失去控制。大多數(shù)軟件災(zāi)難都是從微不足道的小事情開始的,大多數(shù)項(xiàng)目的拖延都是一天一天發(fā)生的。系統(tǒng)一個(gè)特性一個(gè)特性地偏離其規(guī)范,一個(gè)又一個(gè)的補(bǔ)丁被打到某段代碼上,直到最初的代碼一點(diǎn)沒有留下。常常是小事情的累積破壞了士氣和團(tuán)隊(duì)。提示6RemembertheBigPicture記住大圖景我們沒有做過這個(gè)——真的,但有人說,如果你抓一只青蛙放進(jìn)沸水里,它會(huì)一下子跳出來。但是,如果你把青蛙放進(jìn)冷水里,然后慢慢加熱,青蛙不會(huì)注意到溫度的緩慢變化,會(huì)呆在鍋里,直到被煮熟。注意,青蛙的問題與第2節(jié)討論的破窗戶問題不同。在破窗戶理論中,人們失去與熵戰(zhàn)斗的意愿,是因?yàn)樗麄冇X察到?jīng)]有人會(huì)在意。而青蛙只是沒有注意到變化。不要像青蛙一樣。留心大圖景。要持續(xù)不斷地觀察周圍發(fā)生的事情,而不只是你自己在做的事情。相關(guān)內(nèi)容:l軟件的熵,4頁l靠巧合編程,172頁l重構(gòu),184頁l需求之坑,202頁l注重實(shí)效的團(tuán)隊(duì),224頁挑戰(zhàn)l在評(píng)閱本書的草稿時(shí),JohnLakos提出這樣一個(gè)問題:士兵漸進(jìn)地欺騙村民,但他們所催生的變化對(duì)村民完全有利。但是,漸進(jìn)地欺騙青蛙,你是在加害于它。當(dāng)你設(shè)法催生變化時(shí),你能否確定你是在做石頭湯還是青蛙湯?決策是主觀的還是客觀的?4足夠好的軟件欲求更好,常把好事變?cè)??!顮柾?.4有一個(gè)(有點(diǎn))老的笑話,說一家美國公司向一家日本制造商訂購100000片集成電路。規(guī)格說明中有次品率:10000片中只能有1片。幾周過后訂貨到了:一個(gè)大盒子,里面裝有數(shù)千片IC,還有一個(gè)小盒子,里面只裝有10片IC。在小盒子上有一個(gè)標(biāo)簽,上面寫著:“這些是次品”。要是我們真的能這樣控制質(zhì)量就好了。但現(xiàn)實(shí)世界不會(huì)讓我們制作出十分完美的產(chǎn)品,特別是不會(huì)有無錯(cuò)的軟件。時(shí)間、技術(shù)和急躁都在合謀反對(duì)我們。但是,這并不一定就讓人氣餒。如EdYourdon發(fā)表在IEEESoftware上的一篇文章[You95]所描述的,你可以訓(xùn)練你自己,編寫出足夠好的軟件——對(duì)你的用戶、對(duì)未來的維護(hù)者、對(duì)你自己內(nèi)心的安寧來說足夠好。你會(huì)發(fā)現(xiàn),你變得更多產(chǎn),而你的用戶也會(huì)更加高興。你也許還會(huì)發(fā)現(xiàn),因?yàn)椤胺趸凇备?,你的程序?qū)嶋H上更好了。在繼續(xù)前進(jìn)之前,我們需要對(duì)我們將要說的話進(jìn)行限定。短語“足夠好”并非意味著不整潔或制作糟糕的代碼。所有系統(tǒng)都必須滿足其用戶的需求,才能取得成功。我們只是在宣揚(yáng),應(yīng)該給用戶以機(jī)會(huì),讓他們參與決定你所制作的東西何時(shí)已足夠好。讓你的用戶參與權(quán)衡通常你是為別人編寫軟件。你常常需要記得從他們那里獲取需求[2]。但你是否常問他們,他們想要他們的軟件有多好?有時(shí)候選擇并不存在。如果你的工作對(duì)象是心臟起搏器、航天飛機(jī)、或是將被廣泛傳播的底層庫,需求就會(huì)更苛刻,你的選擇就更有限。但是,如果你的工作對(duì)象是全新的產(chǎn)品,你就會(huì)有不同的約束。市場人員有需要信守的承諾,最終用戶也許已基于交付時(shí)間表制定了各種計(jì)劃,而你的公司肯定有現(xiàn)金流方面的約束。無視這些用戶的需求,一味地給程序增加新特性,或是一次又一次潤飾代碼,這不是有職業(yè)素養(yǎng)的做法。我們不是在提倡慌張:許諾不可能兌現(xiàn)的時(shí)間標(biāo)度(timescale),為趕上最后期限而削減基本的工程內(nèi)容,這些同樣不是有職業(yè)素養(yǎng)的做法。你所制作的系統(tǒng)的范圍和質(zhì)量應(yīng)該作為系統(tǒng)需求的一部分規(guī)定下來。提示7MakeQualityaRequirementsIssue使質(zhì)量成為需求問題你常常會(huì)處在須要進(jìn)行權(quán)衡的情形中。讓人驚奇的是,許多用戶寧愿在今天用上有一些“毛邊”的軟件,也不愿等待一年后的多媒體版本。許多預(yù)算吃緊的IT部門都會(huì)同意這樣的說法。今天的了不起的軟件常常比明天的完美軟件更可取。如果你給用戶某樣?xùn)|西,讓他們及早使用,他們的反饋常常會(huì)把你引向更好的最終解決方案(參見曳光彈,48頁)。知道何時(shí)止步在某些方面,編程就像是繪畫。你從空白的畫布和某些基本原材料開始,通過知識(shí)、藝術(shù)和技藝的結(jié)合去確定用前者做些什么。你勾畫出全景,繪制背景,然后填入各種細(xì)節(jié)。你不時(shí)后退一步,用批判的眼光觀察你的作品。常常,你會(huì)扔掉畫布,重新再來。但藝術(shù)家們會(huì)告訴你,如果你不懂得應(yīng)何時(shí)止步,所有的辛苦勞作就會(huì)遭到毀壞。如果你一層又一層、細(xì)節(jié)復(fù)細(xì)節(jié)地疊加,繪畫就會(huì)迷失在繪制之中。不要因?yàn)檫^度修飾和過于求精而毀損完好的程序。繼續(xù)前進(jìn),讓你的代碼憑著自己的質(zhì)量站立一會(huì)兒。它也許不完美,但不用擔(dān)心:它不可能完美(在第6章,171頁,我們將討論在不完美的世界上開發(fā)代碼的哲學(xué))。相關(guān)內(nèi)容:l曳光彈,48頁l需求之坑,202頁l注重實(shí)效的團(tuán)隊(duì),224頁l極大的期待,255頁挑戰(zhàn)l考察你使用的軟件工具和操作系統(tǒng)的制造商。你能否發(fā)現(xiàn)證據(jù),表明這些公司安于發(fā)布他們知道不完美的軟件嗎?作為用戶,你是會(huì)(1)等著他們清除所有bug,(2)擁有復(fù)雜的軟件,并接受某些bug,還是會(huì)(3)選擇缺陷較少的更簡單的軟件?l考慮模塊化對(duì)軟件交付的影響。與以模塊化方式設(shè)計(jì)的系統(tǒng)相比,整體式(monolithic)軟件要達(dá)到所需質(zhì)量,花費(fèi)的時(shí)間更多還是更少?你能找到一個(gè)商業(yè)案例嗎?5你的知識(shí)資產(chǎn)知識(shí)上的投資總能得到最好的回報(bào)?!窘苊鳌じ惶m克林噢,好樣的老富蘭克林——從不會(huì)想不出精練的說教。為什么,如果我們能夠早睡早起,我們就是了不起的程序員——對(duì)嗎?早起的鳥兒有蟲吃,但早起的蟲子呢?然而在這種情況下,Ben確實(shí)命中了要害。你的知識(shí)和經(jīng)驗(yàn)是你最重要的職業(yè)財(cái)富。遺憾的是,它們是有時(shí)效的資產(chǎn)(expiringasset)。隨著新技術(shù)、語言及環(huán)境的出現(xiàn),你的知識(shí)會(huì)變得過時(shí)。不斷變化的市場驅(qū)動(dòng)力也許會(huì)使你的經(jīng)驗(yàn)變得陳舊或無關(guān)緊要。考慮到“網(wǎng)年”飛逝的速度,這樣的事情可能會(huì)非常快地發(fā)生。隨著你的知識(shí)的價(jià)值降低,對(duì)你的公司或客戶來說,你的價(jià)值也在降低。我們想要阻止這樣的事情,決不讓它發(fā)生。你的知識(shí)資產(chǎn)我們喜歡把程序員所知道的關(guān)于計(jì)算技術(shù)和他們所工作的應(yīng)用領(lǐng)域的全部事實(shí)、以及他們的所有經(jīng)驗(yàn)視為他們的知識(shí)資產(chǎn)(KnowledgePortfolios)。管理知識(shí)資產(chǎn)與管理金融資產(chǎn)非常相似:1.嚴(yán)肅的投資者定期投資——作為習(xí)慣。2.多元化是長期成功的關(guān)鍵。3.聰明的投資者在保守的投資和高風(fēng)險(xiǎn)、高回報(bào)的投資之間平衡他們的資產(chǎn)。4.投資者設(shè)法低買高賣,以獲取最大回報(bào)。5.應(yīng)周期性地重新評(píng)估和平衡資產(chǎn)。要在職業(yè)生涯中獲得成功,你必須運(yùn)用同樣的指導(dǎo)方針管理你的知識(shí)資產(chǎn)。經(jīng)營你的資產(chǎn)l定期投資。就像金融投資一樣,你必須定期為你的知識(shí)資產(chǎn)投資。即使投資量很小,習(xí)慣自身也和總量一樣重要。在下一節(jié)中將列出一些示范目標(biāo)。l多元化。你知道的不同的事情越多,你就越有價(jià)值。作為底線,你需要知道你目前所用的特定技術(shù)的各種特性。但不要就此止步。計(jì)算技術(shù)的面貌變化很快——今天的熱門技術(shù)明天就可能變得近乎無用(或至少是不再搶手)。你掌握的技術(shù)越多,你就越能更好地進(jìn)行調(diào)整,趕上變化。l管理風(fēng)險(xiǎn)。從高風(fēng)險(xiǎn)、可能有高回報(bào),到低風(fēng)險(xiǎn)、低回報(bào),技術(shù)存在于這樣一條譜帶上。把你所有的金錢都投入可能突然崩盤的高風(fēng)險(xiǎn)股票并不是一個(gè)好主意;你也不應(yīng)太保守,錯(cuò)過可能的機(jī)會(huì)。不要把你所有的技術(shù)雞蛋放在一個(gè)籃子里。l低買高賣。在新興的技術(shù)流行之前學(xué)習(xí)它可能就和找到被低估的股票一樣困難,但所得到的就和那樣的股票帶來的收益一樣。在Java剛出現(xiàn)時(shí)學(xué)習(xí)它可能有風(fēng)險(xiǎn),但對(duì)于現(xiàn)在已步入該領(lǐng)域的頂尖行列的早期采用者,這樣做得到了非常大的回報(bào)。l重新評(píng)估和平衡。這是一個(gè)非常動(dòng)蕩的行業(yè)。你上個(gè)月開始研究的熱門技術(shù)現(xiàn)在也許已像石頭一樣冰冷。也許你需要重溫你有一陣子沒有使用的數(shù)據(jù)庫技術(shù)。又或許,如果你之前試用過另一種語言,你就會(huì)更有可能獲得那個(gè)新職位……在所有這些指導(dǎo)方針中,最重要的也是最簡單的:提示8InvestRegularlyinYourKnowledgePortfolio定期為你的知識(shí)資產(chǎn)投資目標(biāo)關(guān)于何時(shí)以及增加什么到你的知識(shí)資產(chǎn)中,現(xiàn)在你已經(jīng)擁有了一些指導(dǎo)方針,那么什么是獲得智力資本、從而為你的資產(chǎn)提供資金的最佳方式呢?這里有一些建議。l每年至少學(xué)習(xí)一種新語言。不同語言以不同方式解決相同的問題。通過學(xué)習(xí)若干不同的方法,可以幫助你拓寬你的思維,并避免墨守成規(guī)。此外,現(xiàn)在學(xué)習(xí)許多語言已容易了許多,感謝可從網(wǎng)上自由獲取的軟件財(cái)富(參見267頁)。l每季度閱讀一本技術(shù)書籍。書店里擺滿了許多書籍,討論與你當(dāng)前的項(xiàng)目有關(guān)的有趣話題。一旦你養(yǎng)成習(xí)慣,就一個(gè)月讀一本書。在你掌握了你正在使用的技術(shù)之后,擴(kuò)寬范圍,閱讀一些與你的項(xiàng)目無關(guān)的書籍。l也要閱讀非技術(shù)書籍。記住計(jì)算機(jī)是由人——你在設(shè)法滿足其需要的人——使用的,這十分重要。不要忘了等式中人這一邊。l上課。在本地的學(xué)院或大學(xué)、或是將要來臨的下一次會(huì)展上尋找有趣的課程。l參加本地用戶組織。不要只是去聽講,而要主動(dòng)參與。與世隔絕對(duì)你的職業(yè)生涯來說可能是致命的;打聽一下你們公司以外的人都在做什么。l試驗(yàn)不同的環(huán)境。如果你只在Windows上工作,就在家玩一玩Unix(可自由獲取的Linux就正好)。如果你只用過makefile和編輯器,就試一試IDE,反之亦然。l跟上潮流。訂閱商務(wù)雜志和其他期刊(參見262頁的推薦刊物)。選擇所涵蓋的技術(shù)與你當(dāng)前的項(xiàng)目不同的刊物。l上網(wǎng)。想要了解某種新語言或其他技術(shù)的各種特性?要了解其他人的相關(guān)經(jīng)驗(yàn),了解他們使用的特定行話,等等,新聞組是一種很好的方式。上網(wǎng)沖浪,查找論文、商業(yè)站點(diǎn),以及其他任何你可以找到的信息來源。持續(xù)投入十分重要。一旦你熟悉了某種新語言或新技術(shù),繼續(xù)前進(jìn)。學(xué)習(xí)另一種。是否在某個(gè)項(xiàng)目中使用這些技術(shù),或者是否把它們放入你的簡歷,這并不重要。學(xué)習(xí)的過程將擴(kuò)展你的思維,使你向著新的可能性和新的做事方式拓展。思想的“異花授粉”(cross-pollination)十分重要;設(shè)法把你學(xué)到的東西應(yīng)用到你當(dāng)前的項(xiàng)目中。即使你的項(xiàng)目沒有使用該技術(shù),你或許也能借鑒一些想法。例如,熟悉了面向?qū)ο?,你就?huì)用不同的方式編寫純C程序。學(xué)習(xí)的機(jī)會(huì)于是你狼吞虎咽地閱讀,在你的領(lǐng)域,你站在了所有突破性進(jìn)展的前沿(這不是容易的事情)。有人向你請(qǐng)教一個(gè)問題,答案是什么?你連最起碼的想法都沒有。你坦白地承認(rèn)了這一點(diǎn)。不要就此止步,把找到答案視為對(duì)你個(gè)人的挑戰(zhàn)。去請(qǐng)教古魯(如果在你們的辦公室里沒有,你應(yīng)該能在Internet上找到:參見下一頁上的方框)。上網(wǎng)搜索。去圖書館。如果你自己找不到答案,就去找出能找到答案的人。不要把問題擱在那里。與他人交談可以幫助你建立人際網(wǎng)絡(luò),而因?yàn)樵谶@個(gè)過程中找到了其他不相關(guān)問題的解決方案,你也許還會(huì)讓自己大吃一驚。舊有的資產(chǎn)也在不斷增長……所有閱讀和研究都需要時(shí)間,而時(shí)間已經(jīng)很短缺。所以你需要預(yù)先規(guī)劃。讓自己在空閑的片刻時(shí)間里總有東西可讀?;ㄔ诘柔t(yī)生上的時(shí)間是抓緊閱讀的好機(jī)會(huì)——但一定要帶上你自己的雜志,否則,你也許會(huì)發(fā)現(xiàn)自己在翻閱1973年的一篇卷角的關(guān)于巴布亞新幾內(nèi)亞的文章。批判的思考最后一個(gè)要點(diǎn)是,批判地思考你讀到的和聽到的。你需要確保你的資產(chǎn)中的知識(shí)是準(zhǔn)確的,并且沒有受到供應(yīng)商或媒體炒作的影響。警惕聲稱他們的信條提供了惟一答案的狂熱者——那或許適用、或許不適用于你和你的項(xiàng)目。不要低估商業(yè)主義的力量。Web搜索引擎把某個(gè)頁面列在最前面,并不意味著那就是最佳選擇;內(nèi)容供應(yīng)商可以付錢讓自己排在前面。書店在顯著位置展示某一本書,也并不意味著那就是一本好書,甚至也不說明那是一本受歡迎的書;它們可能是付了錢才放在那里的。提示9CriticallyAnalyzeWhatYouReadandHear批判地分析你讀到的和聽到的遺憾的是,幾乎再?zèng)]有簡單的答案了。但擁有大量知識(shí)資產(chǎn),并把批判的分析應(yīng)用于你將要閱讀的技術(shù)出版物的洪流,你將能夠理解復(fù)雜的答案。與古魯打交道的禮節(jié)與教養(yǎng)隨著Internet在全球普及,古魯們突然變得像你的Enter鍵一樣貼近。那么,你怎樣才能找到一個(gè)古魯,怎樣才能找一個(gè)古魯和你交談呢?我們找到了一些簡單的訣竅。l確切地知道你想要問什么,并盡量明確具體。l小心而得體地組織你的問題。記住你是在請(qǐng)求幫助;不要顯得好像是在要求對(duì)方回答。l組織好問題之后,停下來,再找找答案。選出一些關(guān)鍵字,搜索Web。查找適當(dāng)?shù)腇AQ(常見問題的解答列表)。l決定你是想公開提問還是私下提問。Usenet新聞組是與專家會(huì)面的美妙場所,在那里可以討論幾乎任何問題,但有些人對(duì)這些新聞組的公共性質(zhì)有顧慮。你總是可以用另外的方法:直接發(fā)電子郵件給古魯。不管怎樣,要使用有意義的主題(“需要幫助?。?!”無益于事)。l坐回椅子上,耐心等候。人們很忙,也許需要幾天才能得到明確的答案。最后,請(qǐng)一定要感謝任何回應(yīng)你的人。如果你看到有人提出你能夠解答的問題,盡你的一份力,參與解答。挑戰(zhàn)l這周就開始學(xué)習(xí)一種新語言。總在用C++編程?試試Smalltalk[URL13]或Squeak[URL14]。在用Java?試試Eiffel[URL10]或TOM[URL15]。關(guān)于其他自由編譯器和環(huán)境的來源,參見267頁。l開始閱讀一本新書(但要先讀完這一本!)。如果你在進(jìn)行非常詳細(xì)的實(shí)現(xiàn)和編碼,就閱讀關(guān)于設(shè)計(jì)和架構(gòu)的書。如果你在進(jìn)行高級(jí)設(shè)計(jì),就閱讀關(guān)于編碼技術(shù)的書。l出去和與你的當(dāng)前項(xiàng)目無關(guān)的人、或是其他公司的人談?wù)劶夹g(shù)。在你們公司的自助餐廳里結(jié)識(shí)其他人,或是在本地用戶組織聚會(huì)時(shí)尋找興趣相投的人。6交流!我相信,被打量比被忽略要好?!狹aeWest,BelleoftheNineties,1934也許我們可以從West女士那里學(xué)到一點(diǎn)什么。問題不只是你有什么,還要看你怎樣包裝它。除非你能夠與他人交流,否則就算你擁有最好的主意、最漂亮的代碼、或是最注重實(shí)效的想法,最終也會(huì)毫無結(jié)果。沒有有效的交流,一個(gè)好想法就只是一個(gè)無人關(guān)心的孤兒。作為開發(fā)者,我們必須在許多層面上進(jìn)行交流。我們把許多小時(shí)花在開會(huì)、傾聽和交談上。我們與最終用戶一起工作,設(shè)法了解他們的需要。我們編寫代碼,與機(jī)器交流我們的意圖;把我們的想法變成文檔,留給以后的開發(fā)者。我們撰寫提案和備忘錄,用以申請(qǐng)資源并證明其正當(dāng)性、報(bào)告我們的狀態(tài)、以及提出各種新方法。我們每天在團(tuán)隊(duì)中工作,宣揚(yáng)我們的主意、修正現(xiàn)有的做法、并提出新的做法。我們的時(shí)間有很大一部分都花在交流上,所以我們需要把它做好。我們匯總了我們覺得有用的一些想法。知道你想要說什么在工作中使用的更為正式的交流方式中,最困難的部分也許是確切地弄清楚你想要說什么。小說家在開始寫作之前,會(huì)詳細(xì)地構(gòu)思情節(jié),而撰寫技術(shù)文檔的人卻常常樂于坐到鍵盤前,鍵入“1.介紹……”,并開始敲入接下來在他們的頭腦里冒出來的任何東西。規(guī)劃你想要說的東西。寫出大綱。然后問你自己:“這是否講清了我要說的所有內(nèi)容?”提煉它,直到確實(shí)如此為止。這個(gè)方法不只適用于撰寫文檔。當(dāng)你面臨重要會(huì)議、或是要與重要客戶通電話時(shí),簡略記下你想要交流的想法,并準(zhǔn)備好幾種把它們講清楚的策略。了解你的聽眾只有當(dāng)你是在傳達(dá)信息時(shí),你才是在交流。為此,你需要了解你的聽眾的需要、興趣、能力。我們都曾出席過這樣的會(huì)議:一個(gè)做開發(fā)的滑稽人物在發(fā)表長篇獨(dú)白,講述某種神秘技術(shù)的各種優(yōu)點(diǎn),把市場部副總裁弄得目光呆滯。這不是交流,而只是空談,讓人厭煩的(annoying)空談。要在腦海里形成一幅明確的關(guān)于你的聽眾的畫面。下一頁的圖1.1中顯示的WISDOM離合詩(acrostic)可能會(huì)對(duì)你有幫助。假設(shè)你想提議開發(fā)一個(gè)基于Web的系統(tǒng),用于讓你們的最終用戶提交bug報(bào)告。取決于聽眾的不同,你可以用不同的方式介紹這個(gè)系統(tǒng)。如果可以不用在電話上等候,每天24小時(shí)提交bug報(bào)告,最終用戶將會(huì)很高興。你們的市場部門可以利用這一事實(shí)促銷。支持部門的經(jīng)理會(huì)因?yàn)閮蓚€(gè)原因而高興:所需員工更少,問題報(bào)告得以自動(dòng)化。最后,開發(fā)者會(huì)因?yàn)槟塬@得基于Web的客戶-服務(wù)器技術(shù)和新數(shù)據(jù)庫引擎方面的經(jīng)驗(yàn)而感到享受。通過針對(duì)不同的人進(jìn)行適當(dāng)?shù)男拚?,你將讓他們都為你的?xiàng)目感到興奮。選擇時(shí)機(jī)這是星期五的下午六點(diǎn),審計(jì)人員進(jìn)駐已有一周。你的老板最小的孩子在醫(yī)院里,外面下著滂沱大雨,這時(shí)開車回家肯定是一場噩夢(mèng)。這大概不是向她提出PC內(nèi)存升級(jí)的好時(shí)候。為了了解你的聽眾需要聽到什么,你需要弄清楚他們的“輕重緩急”是什么。找到一個(gè)剛剛因?yàn)閬G失源碼而遭到老板批評(píng)的經(jīng)理,向她介紹你關(guān)于源碼倉庫的構(gòu)想,你將會(huì)擁有一個(gè)更容易接納的傾聽者。要讓你所說的適得其時(shí),在內(nèi)容上切實(shí)相關(guān)。有時(shí)候,只要簡單地問一句“現(xiàn)在我們可以談?wù)劇瓎??”就可以了。圖1.1WISDOM離合詩——了解聽眾Whatdoyouwantthemtolearn?Whatistheirinterestinwhatyou’vegottosay?Howsophisticatedarethey?Howmuchdetaildotheywant?Whomdoyouwanttoowntheinformation?Howcanyoumotivatethemtolistentoyou?你想讓他們學(xué)到什么?他們對(duì)你講的什么感興趣?他們有多富有經(jīng)驗(yàn)?他們想要多少細(xì)節(jié)?你想要讓誰擁有這些信息?你如何促使他們聽你說話?選擇風(fēng)格調(diào)整你的交流風(fēng)格,讓其適應(yīng)你的聽眾。有人要的是正式的“事實(shí)”簡報(bào)。另一些人喜歡在進(jìn)入正題之前高談闊論一番。如果是書面文檔,則有人喜歡一大摞報(bào)告,而另一些人卻喜歡簡單的備忘錄或電子郵件。如果有疑問,就詢問對(duì)方。但是,要記住,你也是交流事務(wù)的一方。如果有人說,他們需要你用一段話進(jìn)行描述,而你覺得不用若干頁紙就無法做到,如實(shí)告訴他們。記住,這樣的反饋也是交流的一種形式。讓文檔美觀你的主意很重要。它們應(yīng)該以美觀的方式傳遞給你的聽眾。太多程序員(和他們的經(jīng)理)在制作書面文檔時(shí)只關(guān)心內(nèi)容。我們認(rèn)為這是一個(gè)錯(cuò)誤。任何一個(gè)廚師都會(huì)告訴你,你可以在廚房里忙碌幾個(gè)小時(shí),最后卻會(huì)因?yàn)轱埐嗽愀獾耐庥^而毀掉你的努力。在今天,已經(jīng)沒有任何借口制作出外觀糟糕的打印文檔?,F(xiàn)代的字處理器(以及像LaTeX和troff這樣的排版系統(tǒng))能夠生成非常好的輸出。你只需要學(xué)習(xí)一些基本的命令。如果你的字處理器支持樣式表,就加以利用(你的公司也許已經(jīng)定義了你可以使用的樣式表)。學(xué)習(xí)如何設(shè)置頁眉和頁腳。查看你的軟件包中包含的樣本文檔,以對(duì)樣式和版式有所了解。檢查拼寫,先自動(dòng),再手工。畢竟,有一些拼寫錯(cuò)誤是檢查器找不出來的(Afterawl,theirarespellingmissstreaksthatthechequercanknotketch)。讓聽眾參與我們常常發(fā)現(xiàn),與制作文檔的過程相比,我們制作出的文檔最后并沒有那么重要。如果可能,讓你的讀者參與文檔的早期草稿的制作。獲取他們的反饋,并汲取他們的智慧。你將建立良好的工作關(guān)系,并很可能在此過程中制作出更好的文檔。做傾聽者如果你想要大家聽你說話,你必須使用一種方法:聽他們說話。即使你掌握著全部信息,即使那是一個(gè)正式會(huì)議,你站在20個(gè)衣著正式的人面前——如果你不聽他們說話,他們也不會(huì)聽你說話。鼓勵(lì)大家通過提問來交談,或是讓他們總結(jié)你告訴他們的東西。把會(huì)議變成對(duì)話,你將能更有效地闡明你的觀點(diǎn)。誰知道呢,你也許還能學(xué)到點(diǎn)什么?;貜?fù)他人如果你向別人提問,他們不做出回應(yīng),你會(huì)覺得他們不禮貌。但當(dāng)別人給你發(fā)送電子郵件或備忘錄、請(qǐng)你提供信息、或是采取某種行動(dòng)時(shí),你是否經(jīng)常忘記回復(fù)?在匆忙的日常生活中,很容易忘記事情。你應(yīng)該總是對(duì)電子郵件和語音郵件做出回應(yīng),即使內(nèi)容只是“我稍后回復(fù)你。”隨時(shí)通知?jiǎng)e人,會(huì)讓他們更容易原諒你偶然的疏忽,并讓他們覺得你沒有忘記他們。提示10It’sBothWhatYouSayandtheWayYouSayIt你說什么和你怎么說同樣重要除非你生活在真空中,你才不需要能交流。交流越有效,你就越有影響力。電子郵件交流我們所說的關(guān)于書面交流的所有東西都同樣適用于電子郵件?,F(xiàn)在的電子郵件已經(jīng)發(fā)展成為公司內(nèi)部和公司之間進(jìn)行交流的主要手段。它被用于討論合約、調(diào)解爭端,以及用作法庭證據(jù)。但因?yàn)槟撤N原因,許多從不會(huì)發(fā)出低劣的書面文檔的人卻樂于往全世界亂扔外觀糟糕的電子郵件。我們關(guān)于電子郵件的提示很簡單:l在你按下SEND之前進(jìn)行校對(duì)。l檢查拼寫。l讓格式保持簡單。有人使用均衡字體(proportionalfont)閱讀電子郵件,所以你辛苦制作的ASCII藝術(shù)圖形在他們看來將像是母雞的腳印一樣亂七八糟。l只在你知道對(duì)方能夠閱讀rich-text或HTML格式的郵件的情況下使用這些格式。純文本是通用的。l設(shè)法讓引文減至最少。沒有人喜歡收到一封回郵,其中有100行是他原來的電子郵件,只在最后新添了三個(gè)字:“我同意”。l如果你引用別人的電子郵件,一定要注明出處。并在正文中進(jìn)行引用(而不是當(dāng)做附件)。l不要用言語攻擊別人(flame),除非你想讓別人也攻擊你,并老是糾纏你。l在發(fā)送之前檢查你的收件人名單。最近《華爾街日?qǐng)?bào)》上有一篇文章報(bào)道說,有一個(gè)雇員通過部門的電子郵件散布對(duì)老板的不滿,卻沒有意識(shí)到老板也在收件人名單里。l將你的電子郵件——你收到的重要文件和你發(fā)送的郵件——加以組織并存檔。如Microsoft和Netscape的好些雇員在1999年司法部調(diào)查期間所發(fā)現(xiàn)的,e-mail是永久性的。要設(shè)法像對(duì)待任何書面?zhèn)渫浕驁?bào)告一樣小心對(duì)待e-mail??偨Y(jié)l知道你想要說什么。l了解你的聽眾。l選擇時(shí)機(jī)。l選擇風(fēng)格。l讓文檔美觀。l讓聽眾參與。l做傾聽者。l回復(fù)他人。相關(guān)內(nèi)容:l原型與便箋,53頁l注重實(shí)效的團(tuán)隊(duì),224頁挑戰(zhàn)l有幾本好書討論了開發(fā)團(tuán)隊(duì)內(nèi)部的交流[Bro95,McC95,DL99]。下決心在接下來的18個(gè)月里讀完所有這三本書。此外,DinosaurBrains[Ber96]這本書討論了我們所有人都會(huì)帶到工作環(huán)境中的“情緒包袱”。l在你下一次進(jìn)行展示、或是撰寫備忘錄支持某種立場時(shí),先試著按第20頁的WISDOM離合詩做一遍??催@樣是否有助于你了解怎樣定位你的講話。如果合適,事后與你的聽眾談一談,看你對(duì)他們的需要的估計(jì)有多準(zhǔn)確。7重復(fù)的危害有些提示和訣竅可應(yīng)用于軟件開發(fā)的所有層面,有些想法幾乎是公理,有些過程實(shí)際上普遍適用。但是,人們幾乎沒有為這些途徑建立這樣的文檔,你很可能會(huì)發(fā)現(xiàn),它們作為零散的段落寫在關(guān)于設(shè)計(jì)、項(xiàng)目管理或編碼的討論中。在這一章里,我們將要把這些想法和過程集中在一起。頭兩節(jié),“重復(fù)的危害”與“正交性”,密切相關(guān)。前者提醒你,不要在系統(tǒng)各處對(duì)知識(shí)進(jìn)行重復(fù),后者提醒你,不要把任何一項(xiàng)知識(shí)分散在多個(gè)系統(tǒng)組件中。隨著變化的步伐加快,我們?cè)絹碓诫y以讓應(yīng)用跟上變化。在“可撤消性”中,我們將考察有助于使你的項(xiàng)目與其不斷變化的環(huán)境絕緣的一些技術(shù)。接下來的兩節(jié)也是相關(guān)的。在“曳光彈”中,我們將討論一種開發(fā)方式,能讓你同時(shí)搜集需求、測試設(shè)計(jì)、并實(shí)現(xiàn)代碼。這聽起來太好,不可能是真的?的確如此:曳光彈開發(fā)并非總是可以應(yīng)用?!霸团c便箋”將告訴你,在曳光彈開發(fā)不適用的情況下,怎樣使用原型來測試架構(gòu)、算法、接口以及各種想法。隨著計(jì)算機(jī)科學(xué)慢慢成熟,設(shè)計(jì)者正在制作越來越高級(jí)的語言。盡管能夠接受“讓它這樣”(makeitso)指令的編譯器還沒有發(fā)明出來,在“領(lǐng)域語言”中我們給出了一些適度的建議,你可以自行加以實(shí)施。最后,我們都是在一個(gè)時(shí)間和資源有限的世界上工作。如果你善于估計(jì)出事情需要多長時(shí)間完成,你就能更好地在兩者都很匱乏的情況下生存下去(并讓你的老板更高興)。我們將在“估算”中涵蓋這一主題。在開發(fā)過程中牢記這些基本原則,你就將能編寫更快、更好、更強(qiáng)健的代碼。你甚至可以讓這看起來很容易。7重復(fù)的危害給予計(jì)算機(jī)兩項(xiàng)自相矛盾的知識(shí),是JamesT.Kirk艦長(出自StarTrek,“星際迷航”——譯注)喜歡用來使四處劫掠的人工智能生命失效的方法。遺憾的是,同樣的原則也能有效地使你的代碼失效。作為程序員,我們收集、組織、維護(hù)和利用知識(shí)。我們?cè)谝?guī)范中記載知識(shí)、在運(yùn)行的代碼中使其活躍起來并將其用于提供測試過程中所需的檢查。遺憾的是,知識(shí)并不穩(wěn)定。它變化——常常很快。你對(duì)需求的理解可能會(huì)隨著與客戶的會(huì)談而發(fā)生變化。政府改變規(guī)章制度,有些商業(yè)邏輯過時(shí)了。測試也許表明所選擇的算法無法工作。所有這些不穩(wěn)定都意味著我們要把很大一部分時(shí)間花在維護(hù)上,重新組織和表達(dá)我們的系統(tǒng)中的知識(shí)。大多數(shù)人都以為維護(hù)是在應(yīng)用發(fā)布時(shí)開始的,維護(hù)就意味著修正bug和增強(qiáng)特性。我們認(rèn)為這些人錯(cuò)了。程序員須持續(xù)不斷地維護(hù)。我們的理解逐日變化。當(dāng)我們?cè)O(shè)計(jì)或編碼時(shí),出現(xiàn)了新的需求。環(huán)境或許變了。不管原因是什么,維護(hù)都不是時(shí)有時(shí)無的活動(dòng),而是整個(gè)開發(fā)過程中的例行事務(wù)。當(dāng)我們進(jìn)行維護(hù)時(shí),我們必須找到并改變事物的表示——那些嵌在應(yīng)用中的知識(shí)膠囊。問題是,在我們開發(fā)的規(guī)范、過程和程序中很容易重復(fù)表述知識(shí),而當(dāng)我們這樣做時(shí),我們是在向維護(hù)的噩夢(mèng)發(fā)出邀請(qǐng)——在應(yīng)用發(fā)布之前就會(huì)開始的噩夢(mèng)。我們覺得,可靠地開發(fā)軟件、并讓我們的開發(fā)更易于理解和維護(hù)的惟一途徑,是遵循我們稱之為DRY的原則:系統(tǒng)中的每一項(xiàng)知識(shí)都必須具有單一、無歧義、權(quán)威的表示。我們?yōu)楹畏Q其為DRY?提示11DRY–Don’tRepeatYourself不要重復(fù)你自己與此不同的做法是在兩個(gè)或更多地方表達(dá)同一事物。如果你改變其中一處,你必須記得改變其他各處。或者,就像那些異形計(jì)算機(jī),你的程序?qū)⒁驗(yàn)樽韵嗝芏黄惹?。這不是你是否能記住的問題,而是你何時(shí)忘記的問題。你會(huì)發(fā)現(xiàn)DRY原則在全書中一再出現(xiàn),并且常常出現(xiàn)在與編碼無關(guān)的語境中。我們覺得,這是注重實(shí)效的程序員的工具箱里最重要的工具之一。在這一節(jié)我們將概述重復(fù)的問題,并提出對(duì)此加以處理的一般策略。重復(fù)是怎樣發(fā)生的我們所見到的大多數(shù)重復(fù)都可歸入下列范疇:l強(qiáng)加的重復(fù)(imposedduplication)。開發(fā)者覺得他們無可選擇——環(huán)境似乎要求重復(fù)。l無意的重復(fù)(inadvertentduplication)。開發(fā)者沒有意識(shí)到他們?cè)谥貜?fù)信息。l無耐性的重復(fù)(impatientduplication)。開發(fā)者偷懶,他們重復(fù),因?yàn)槟菢铀坪醺菀?。l開發(fā)者之間的重復(fù)(interdeveloperduplication)。同一團(tuán)隊(duì)(或不同團(tuán)隊(duì))的幾個(gè)人重復(fù)了同樣的信息。讓我們更詳細(xì)地看一看這四個(gè)以“i”開頭的重復(fù)。強(qiáng)加的重復(fù)有時(shí),重復(fù)似乎是強(qiáng)加給我們的。項(xiàng)目標(biāo)準(zhǔn)可能要求建立含有重復(fù)信息的文檔,或是重復(fù)代碼中的信息的文檔。多個(gè)目標(biāo)平臺(tái)各自需要自己的編程語言、庫以及開發(fā)環(huán)境,這會(huì)使我們重復(fù)共有的定義和過程。編程語言自身要求某些重復(fù)信息的結(jié)構(gòu)。我們都在我們覺得無力避免重復(fù)的情形下工作過。然而也有一些方法,可用于把一項(xiàng)知識(shí)存放在一處,以遵守DRY原則,同時(shí)也讓我們的生活更容易一點(diǎn)。這里有一些這樣的技術(shù):信息的多種表示。在編碼一級(jí),我們常常需要以不同的形式表示同一信息。我們也許在編寫客戶-服務(wù)器應(yīng)用,在客戶和服務(wù)器端使用了不同的語言,并且需要在兩端都表示某種共有的結(jié)構(gòu)。我們或許需要一個(gè)類,其屬性是某個(gè)數(shù)據(jù)庫表的schema(模型、方案)的鏡像。你也許在撰寫一本書,其中包括的程序片段,也正是你要編譯并測試的程序。發(fā)揮一點(diǎn)聰明才智,你通常能夠消除重復(fù)的需要。答案常常是編寫簡單的過濾器或代碼生成器。可以在每次構(gòu)建(build)軟件時(shí),使用簡單的代碼生成器,根據(jù)公共的元數(shù)據(jù)表示構(gòu)建多種語言下的結(jié)構(gòu)(示例參見圖3.4,106頁)??梢愿鶕?jù)在線數(shù)據(jù)庫schema、或是最初用于構(gòu)建schema的元數(shù)據(jù),自動(dòng)生成類定義。本書中摘錄的代碼,由預(yù)處理器在我們每次對(duì)文本進(jìn)行格式化時(shí)插入。訣竅是讓該過程成為主動(dòng)的,這不能是一次性轉(zhuǎn)換,否則我們就會(huì)退回到重復(fù)數(shù)據(jù)的情況。代碼中的文檔。程序員被教導(dǎo)說,要給代碼加上注釋:好代碼有許多注釋。遺憾的是,沒有人教給他們,代碼為什么需要注釋:糟糕的代碼才需要許多注釋。DRY法則告訴我們,要把低級(jí)的知識(shí)放在代碼中,它屬于那里;把注釋保留給其他的高級(jí)說明。否則,我們就是在重復(fù)知識(shí),而每一次改變都意味著既要改變代碼,也要改變注釋。注釋將不可避免地變得過時(shí),而不可信任的注釋比完全沒有注釋更糟(關(guān)于注釋的更多信息,參見全都是寫,248頁)。文檔與代碼。你撰寫文檔,然后編寫代碼。有些東西變了,你修訂文檔、更新代碼。文檔和代碼都含有同一知識(shí)的表示。而我們都知道,在最緊張的時(shí)候——最后期限在逼近,重要的客戶在喊叫——我們往往會(huì)推遲文檔的更新。Dave曾經(jīng)參與過一個(gè)國際電報(bào)交換機(jī)項(xiàng)目的開發(fā)。很容易理解,客戶要求提供詳盡的測試規(guī)范,并要求軟件在每次交付時(shí)都通過所有測試。為了確保測試準(zhǔn)確地反映規(guī)范,開發(fā)團(tuán)隊(duì)用程序方式、根據(jù)文檔本身生成這些測試。當(dāng)客戶修訂他們的規(guī)范時(shí),測試套件會(huì)自動(dòng)改變。有一次團(tuán)隊(duì)向客戶證明了,該過程很健全,生成驗(yàn)收測試在典型情況下只需要幾秒種。語言問題。許多語言會(huì)在源碼中強(qiáng)加可觀的重復(fù)。如果語言使模塊的接口與其實(shí)現(xiàn)分離,就常常會(huì)出現(xiàn)這樣的情況。C與C++有頭文件,在其中重復(fù)了被導(dǎo)出變量、函數(shù)和(C++的)類的名稱和類型信息。ObjectPascal甚至?xí)谕晃募镏貜?fù)這些信息。如果你使用遠(yuǎn)地過程調(diào)用或CORBA[URL29],你將會(huì)在接口規(guī)范與實(shí)現(xiàn)它的代碼之間重復(fù)接口信息。沒有什么簡單的技術(shù)可用于克服語言的這些需求。盡管有些開發(fā)環(huán)境通過自動(dòng)生成頭文件、隱藏了對(duì)頭文件的需要,而ObjectPascal允許你縮寫重復(fù)的函數(shù)聲明,你通常仍受制于給予你的東西。至少對(duì)于大多數(shù)與語言有關(guān)的問題,與實(shí)現(xiàn)不一致的頭文件將會(huì)產(chǎn)生某種形式的編譯或鏈接錯(cuò)誤。你仍會(huì)弄錯(cuò)事情,但至少,你將在很早的時(shí)候就得到通知。再思考一下頭文件和實(shí)現(xiàn)文件中的注釋。絕對(duì)沒有理由在這兩種文件之間重復(fù)函數(shù)或類頭注釋(headercomment)。應(yīng)該用頭文件記載接口問題,用實(shí)現(xiàn)文件記載代碼的使用者無須了解的實(shí)際細(xì)節(jié)。無意的重復(fù)有時(shí),重復(fù)來自設(shè)計(jì)中的錯(cuò)誤。讓我們看一個(gè)來自配送行業(yè)的例子。假定我們的分析揭示,一輛卡車有車型、牌照號(hào)、司機(jī)及其他一些屬性。與此類似,發(fā)運(yùn)路線的屬性包括路線、卡車和司機(jī)。基于這一理解,我們編寫了一些類。但如果Sally打電話請(qǐng)病假、我們必須改換司機(jī),事情又會(huì)怎樣呢?Truck和DeliverRoute都包含有司機(jī)。我們改變哪一個(gè)?顯然這樣的重復(fù)很糟糕。根據(jù)底層的商業(yè)模型對(duì)其進(jìn)行規(guī)范化(normalize)——卡車的底層屬性集真的應(yīng)包含司機(jī)?路線呢?又或許我們需要第三種對(duì)象,把司機(jī)、卡車及路線結(jié)合在一起。不管最終的解決方案是什么,我們都應(yīng)避免這種不規(guī)范的數(shù)據(jù)。當(dāng)我們擁有多個(gè)互相依賴的數(shù)據(jù)元素時(shí),會(huì)出現(xiàn)一種不那么顯而易見的不規(guī)范數(shù)據(jù)。讓我們看一個(gè)表示線段的類:classLine{public:Pointstart;Pointend;doublelength;};第一眼看上去,這個(gè)類似乎是合理的。線段顯然有起點(diǎn)和終點(diǎn),并總是有長度(即使長度為零)。但這里有重復(fù)。長度是由起點(diǎn)和終點(diǎn)決定的:改變其中一個(gè),長度就會(huì)變化。最好是讓長度成為計(jì)算字段:classLine{public:Pointstart;Pointend;doublelength(){returnstart.distanceTo(end);}};在以后的開發(fā)過程中,你可以因?yàn)樾阅茉蚨x擇違反DRY原則。這經(jīng)常會(huì)發(fā)生在你需要緩存數(shù)據(jù),以避免重復(fù)昂貴的操作時(shí)。其訣竅是使影響局部化。對(duì)DRY原則的違反沒有暴露給外界:只有類中的方法需要注意“保持行為良好”。classLine{private:boolchanged;doublelength;Pointstart;Pointend;public:voidsetStart(Pointp){start=p;changed=true;}voidsetEnd(Pointp){end=p;changed=true;}PointgetStart(void){returnstart;}PointgetEnd(void){returnend;}doublegetLength(){if(changed){length=start.distanceTo(end);changed=false;}returnlength;}};這個(gè)例子還說明了像Java和C++這樣的面向?qū)ο笳Z言的一個(gè)重要問題。在可能的情況下,應(yīng)該總是用訪問器(accessor)函數(shù)讀寫對(duì)象的屬性。這將使未來增加功能(比如緩存)變得更容易。無耐性的重復(fù)每個(gè)項(xiàng)目都有時(shí)間壓力——這是能夠驅(qū)使我們中間最優(yōu)秀的人走捷徑的力量。需要與你寫過的一個(gè)例程相似的例程?你會(huì)受到誘惑,去拷貝原來的代碼,并做出一些改動(dòng)。需要一個(gè)表示最大點(diǎn)數(shù)的值?如果我改動(dòng)頭文件,整個(gè)項(xiàng)目就得重新構(gòu)建。也許我應(yīng)該在這里使用直接的數(shù)字(literalnumber),這里,還有這里,需要一個(gè)與Javaruntime中的某個(gè)類相似的類?源碼在那里(你有使用許可),那么為什么不拷貝它、并做出你所需的改動(dòng)呢?如果你覺得受到誘惑,想一想古老的格言:“欲速則不達(dá)”。你現(xiàn)在也許可以節(jié)省幾秒鐘,但以后卻可能損失幾小時(shí)。想一想圍繞著Y2K慘敗的種種問題。其中許多問題是由開發(fā)者的懶惰造成的:他們沒有參數(shù)化日期字段的尺寸,或是實(shí)現(xiàn)集中的日期服務(wù)庫。無耐性的重復(fù)是一種容易檢測和處理的重復(fù)形式,但那需要你接受訓(xùn)練,并愿意為避免以后的痛苦而預(yù)先花一些時(shí)間。開發(fā)者之間的重復(fù)另一方面,或許是最難檢測和處理的重復(fù)發(fā)生在項(xiàng)目的不同開發(fā)者之間。整個(gè)功能集都可能在無意中被重復(fù),而這些重復(fù)可能幾年里都不會(huì)被發(fā)現(xiàn),從而導(dǎo)致各種維護(hù)問題。我們親耳聽說過,美國某個(gè)州在對(duì)政府的計(jì)算機(jī)系統(tǒng)進(jìn)行Y2K問題檢查時(shí),審計(jì)者發(fā)現(xiàn)有超出10,000個(gè)程序,每一個(gè)都有自己的社會(huì)保障號(hào)驗(yàn)證代碼。在高層,可以通過清晰的設(shè)計(jì)、強(qiáng)有力的技術(shù)項(xiàng)目領(lǐng)導(dǎo)(參見288頁“注重實(shí)效的團(tuán)隊(duì)”一節(jié)中的內(nèi)容)、以及在設(shè)計(jì)中進(jìn)行得到了充分理解的責(zé)任劃分,對(duì)這個(gè)問題加以處理。但是,在模塊層,問題更加隱蔽。不能劃入某個(gè)明顯的責(zé)任區(qū)域的常用功能和數(shù)據(jù)可能會(huì)被實(shí)現(xiàn)許多次。我們覺得,處理這個(gè)問題的最佳方式是鼓勵(lì)開發(fā)者相互進(jìn)行主動(dòng)的交流。設(shè)置論壇,用以討論常見問題(在過去的一些項(xiàng)目中,我們?cè)O(shè)置了私有的Usenet新聞組,用于讓開發(fā)者交換意見,進(jìn)行提問。這提供了一種不受打擾的交流方式——甚至跨越多個(gè)站點(diǎn)——同時(shí)又保留了所有言論的永久歷史)。讓某個(gè)團(tuán)隊(duì)成員擔(dān)任項(xiàng)目資料管理員,其工作是促進(jìn)知識(shí)的交流。在源碼樹中指定一個(gè)中央?yún)^(qū)域,用于存放實(shí)用例程和腳本。一定要閱讀他人的源碼與文檔,不管是非正式的,還是進(jìn)行代碼復(fù)查。你不是在窺探——你是在向他們學(xué)習(xí)。而且要記住,訪問是互惠的——不要因?yàn)閯e人鉆研(亂鉆?)你的代碼而苦惱。提示12MakeItEasytoReuse讓復(fù)用變得容易你所要做的是營造一種環(huán)境,在其中要找到并復(fù)用已有的東西,比自己編寫更容易。如果不容易,大家就不會(huì)去復(fù)用。而如果不進(jìn)行復(fù)用,你們就會(huì)有重復(fù)知識(shí)的風(fēng)險(xiǎn)。相關(guān)內(nèi)容:l正交性,34頁l文本操縱,99頁l代碼生成器,102頁l重構(gòu),184頁l注重實(shí)效的團(tuán)隊(duì),224頁l無處不在的自動(dòng)化,230頁l全都是寫,248頁8正交性如果你想要制作易于設(shè)計(jì)、構(gòu)建、測試及擴(kuò)展的系統(tǒng),正交性是一個(gè)十分關(guān)鍵的概念,但是,正交性的概念很少被直接講授,而常常是你學(xué)習(xí)的各種其他方法和技術(shù)的隱含特性。這是一個(gè)錯(cuò)誤。一旦你學(xué)會(huì)了直接應(yīng)用正交性原則,你將發(fā)現(xiàn),你制作的系統(tǒng)的質(zhì)量立刻就得到了提高。什么是正交性“正交性”是從幾何學(xué)中借來的術(shù)語。如果兩條直線相交成直角,它們就是正交的,比如圖中的坐標(biāo)軸。用向量術(shù)語說,這兩條直線互不依賴。沿著某一條直線移動(dòng),你投影到另一條直線上的位置不變。在計(jì)算技術(shù)中,該術(shù)語用于表示某種不相依賴性或是解耦性。如果兩個(gè)或更多事物中的一個(gè)發(fā)生變化,不會(huì)影響其他事物,這些事物就是正交的。在設(shè)計(jì)良好的系統(tǒng)中,數(shù)據(jù)庫代碼與用戶界面是正交的:你可以改動(dòng)界面,而不影響數(shù)據(jù)庫;更換數(shù)據(jù)庫,而不用改動(dòng)界面。在我們考察正交系統(tǒng)的好處之前,讓我們先看一看非正交系統(tǒng)。非正交系統(tǒng)你正乘坐直升機(jī)游覽科羅拉多大峽谷,駕駛員——他顯然犯了一個(gè)錯(cuò)誤,在吃魚,他的午餐——突然呻吟起來,暈了過去。幸運(yùn)的是,他把你留在了離地面100英尺的地方。你推斷,升降桿控制總升力,所以輕輕將其壓低可以讓直升機(jī)平緩降向地面。然而,當(dāng)你這樣做時(shí),卻發(fā)現(xiàn)生活并非那么簡單。直升機(jī)的鼻子向下,開始向左盤旋下降。突然間你發(fā)現(xiàn),你駕駛的這個(gè)系統(tǒng),所有的控制輸入都有次級(jí)效應(yīng)。壓低左手的操作桿,你需要補(bǔ)償性地向后移動(dòng)右手柄,并踩右踏板。但這些改變中的每一項(xiàng)都會(huì)再次影響所有其他的控制。突然間,你在用一個(gè)讓人難以置信的復(fù)雜系統(tǒng)玩雜耍,其中每一項(xiàng)改變都會(huì)影響所有其他的輸入。你的工作負(fù)擔(dān)異常巨大:你的手腳在不停地移動(dòng),試圖平衡所有交互影響的力量。直升機(jī)的各個(gè)控制器斷然不是正交的。正交的好處如直升機(jī)的例子所闡明的,非正交系統(tǒng)的改變與控制更復(fù)雜是其固有的性質(zhì)。當(dāng)任何系統(tǒng)的各組件互相高度依賴時(shí),就不再有局部修正(localfix)這樣的事情。提示13EliminateEffectsBetweenUnrelatedThings消除無關(guān)事物之間的影響我們想要設(shè)計(jì)自足(self-contained)的組件:獨(dú)立,具有單一、良好定義的目的(Yourdon和Constantine稱之為內(nèi)聚(cohesion)[YC86])。如果組件是相互隔離的,你就知道你能夠改變其中之一,而不用擔(dān)心其余組件。只要你不改變組件的外部接口,你就可以放心:你不會(huì)造成波及整個(gè)系統(tǒng)的問題。如果你編寫正交的系統(tǒng),你得到兩個(gè)主要好處:提高生產(chǎn)率與降低風(fēng)險(xiǎn)。提高生產(chǎn)率l改動(dòng)得以局部化,所以開發(fā)時(shí)間和測試時(shí)間得以降低。與編寫單個(gè)的大塊代碼相比,編寫多個(gè)相對(duì)較小的、自足的組件更為容易。你可以設(shè)計(jì)、編寫簡單的組件,對(duì)其進(jìn)行單元測試,然后把它們忘掉——當(dāng)你增加新代碼時(shí),無須不斷改動(dòng)已有的代碼。l正交的途徑還能夠促進(jìn)復(fù)用。如果組件具有明確而具體的、良好定義的責(zé)任,就可以用其最初的實(shí)現(xiàn)者未曾想象過的方式,把它們與新組件組合在一起。l如果你對(duì)正交的組件進(jìn)行組合,生產(chǎn)率會(huì)有相當(dāng)微妙的提高。假定某個(gè)組件做M件事情,而另一個(gè)組件做N件事情。如果它們是正交的,而你把它們組合在一起,結(jié)果就能做MxN件事情。但是,如果這兩個(gè)組件是非正交的,它們就會(huì)重疊,結(jié)果能做的事情就更少。通過組合正交的組件,你的每一份努力都能得到更多的功能。降低風(fēng)險(xiǎn)正交的途徑能降低任何開發(fā)中固有的風(fēng)險(xiǎn)。l有問題的代碼區(qū)域被隔離開來。如果某個(gè)模塊有毛病,它不大可能把病癥擴(kuò)散到系統(tǒng)的其余部分。要把它切掉,換成健康的新模塊也更容易。l所得系統(tǒng)更健壯。對(duì)特定區(qū)域做出小的改動(dòng)與修正,你所導(dǎo)致的任何問題都將局限在該區(qū)域中。l正交系統(tǒng)很可能能得到更好的測試,因?yàn)樵O(shè)計(jì)測試、并針對(duì)其組件運(yùn)行測試更容易。l你不會(huì)與特定的供應(yīng)商、產(chǎn)品、或是平臺(tái)緊綁在一起,因?yàn)榕c這些第三方組件的接口將被隔離在全部開發(fā)的較小部分中。讓我們看一看在工作中應(yīng)用正交原則的幾種方式。項(xiàng)目團(tuán)隊(duì)你是否注意到,有些項(xiàng)目團(tuán)隊(duì)很有效率,每個(gè)人都知道要做什么,并全力做出貢獻(xiàn),而另一些團(tuán)隊(duì)的成員卻老是在爭吵,而且好像無法避免互相妨礙?這常常是一個(gè)正交性問題。如果團(tuán)隊(duì)的組織有許多重疊,各個(gè)成員就會(huì)對(duì)責(zé)任感到困惑。每一次改動(dòng)都需要整個(gè)團(tuán)隊(duì)開一次會(huì),因?yàn)樗麄冎械娜魏我粋€(gè)人都可能受到影響。怎樣把團(tuán)隊(duì)劃分為責(zé)任得到了良好定義的小組,并使重疊降至最低呢?沒有簡單的答案。這部分地取決于項(xiàng)目本身,以及你對(duì)可能變動(dòng)的區(qū)域的分析。這還取決于你可以得到的人員。我們的偏好是從使基礎(chǔ)設(shè)施與應(yīng)用分離開始。每個(gè)主要的基礎(chǔ)設(shè)施組件(數(shù)據(jù)庫、通信接口、中間件層,等等)有自己的子團(tuán)隊(duì)。如果應(yīng)用功能的劃分顯而易見,那就照此劃分。然后我們考察我們現(xiàn)有的(或計(jì)劃有的)人員,并對(duì)分組進(jìn)行相應(yīng)的調(diào)整。你可以對(duì)項(xiàng)目團(tuán)隊(duì)的正交性進(jìn)行非正式的衡量。只要看一看,在討論每個(gè)所需改動(dòng)時(shí)需要涉及多少人。人數(shù)越多,團(tuán)隊(duì)的正交性就越差。顯然,正交的團(tuán)隊(duì)效率也更高(盡管如此,我們也鼓勵(lì)子團(tuán)隊(duì)不斷地相互交流)。設(shè)計(jì)大多數(shù)開發(fā)者都熟知需要設(shè)計(jì)正交的系統(tǒng),盡管他們可能會(huì)使用像模塊化、基于組件、或是分層這樣的術(shù)語描述該過程。系統(tǒng)應(yīng)該由一組相互協(xié)作的模塊組成,每個(gè)模塊都實(shí)現(xiàn)不依賴于其他模塊的功能。有時(shí),這些組件被組織為多個(gè)層次,每層提供一級(jí)抽象。這種分層的途徑是設(shè)計(jì)正交系統(tǒng)的強(qiáng)大方式。因?yàn)槊繉佣贾皇褂迷谄湎旅娴膶哟翁峁┑某橄?,在改?dòng)底層實(shí)現(xiàn)、而又不影響其他代碼方面,你擁有極大的靈活性。分層也降低了模塊間依賴關(guān)系失控的風(fēng)險(xiǎn)。你將常??吹较裣乱豁摰膱D2.1這樣的圖表示的層次關(guān)系。對(duì)于正交設(shè)計(jì),有一種簡單的測試方法。一旦設(shè)計(jì)好組件,問問你自己:如果我顯著地改變某個(gè)特定功能背后的需求,有多少模塊會(huì)受影響?在正交系統(tǒng)中,答案應(yīng)圖2.1典型的層次圖該是“一個(gè)”。移動(dòng)GUI面板上的按鈕,不應(yīng)該要求改動(dòng)數(shù)據(jù)庫schema。增加語境敏感的幫助,也不應(yīng)該改動(dòng)記賬子系統(tǒng)。讓我們考慮一個(gè)用于監(jiān)視和控制供暖設(shè)備的復(fù)雜系統(tǒng)。原來的需求要求提供圖形用戶界面,但后來需求被改為要增加語音應(yīng)答系統(tǒng),用按鍵電話控制設(shè)備。在正交地設(shè)計(jì)的系統(tǒng)中,你只需要改變那些與用戶界面有關(guān)聯(lián)的模塊,讓它們對(duì)此加以處理:控制設(shè)備的底層邏輯保持不變。事實(shí)上,如果你仔細(xì)設(shè)計(jì)你的系統(tǒng)結(jié)構(gòu),你應(yīng)該能夠用同一個(gè)底層代碼庫支持這兩種界面。157頁的“它只是視圖”將討論怎樣使用模型-視圖-控制器(MVC)范型編寫解耦的代碼,該范型在這里的情況下也能很好地工作。還要問問你自己,你的設(shè)計(jì)在多大程度上解除了與現(xiàn)實(shí)世界中的的變化的耦合?你在把電話號(hào)碼當(dāng)作顧客標(biāo)識(shí)符嗎?如果電話公司重新分配了區(qū)號(hào),會(huì)怎么樣?不要依賴你無法控制的事物屬性。8正交性(2)工具箱與庫在你引入第三方工具箱和庫時(shí),要注意保持系統(tǒng)的正交性。要明智地選擇技術(shù)。我們?cè)?jīng)參加過一個(gè)項(xiàng)目,在其中需要一段Java代碼,既運(yùn)行在本地的服務(wù)器機(jī)器上,又運(yùn)行在遠(yuǎn)地的客戶機(jī)器上。要把類按這樣的方式分布,可以選用RMI或CORBA。如果用RMI實(shí)現(xiàn)類的遠(yuǎn)地訪問,對(duì)類中的遠(yuǎn)地方法的每一次調(diào)用都可能會(huì)拋出異常;這意味著,一個(gè)幼稚的實(shí)現(xiàn)可能會(huì)要求我們,無論何時(shí)使用遠(yuǎn)地類,都要對(duì)異常進(jìn)行處理。在這里,使用RMI顯然不是正交的:調(diào)用遠(yuǎn)地類的代碼應(yīng)該不用知道這些類的位置。另一種方法——使用CORBA——就沒有施加這樣的限制:我們可以編寫不知道我們類的位置的代碼。在引入某個(gè)工具箱時(shí)(甚或是來自你們團(tuán)隊(duì)其他成員的庫),問問你自己,它是否會(huì)迫使你對(duì)代碼進(jìn)行不必要的改動(dòng)。如果對(duì)象持久模型(objectpersistencescheme)是透明的,那么它就是正交的。如果它要求你以一種特殊的方式創(chuàng)建或訪問對(duì)象,那么它就不是正交的。讓這樣的細(xì)節(jié)與代碼隔離具有額外的好處:它使得你在以后更容易更換供應(yīng)商。EnterpriseJavaBeans(EJB)系統(tǒng)是正交性的一個(gè)有趣例子。在大多數(shù)面向事務(wù)的系統(tǒng)中,應(yīng)用代碼必須描述每個(gè)事務(wù)的開始與結(jié)束。在EJB中,該信息是作為元數(shù)據(jù),在任何代碼之外,以聲明的方式表示的。同一應(yīng)用代碼不用修改,就可以運(yùn)行在不同的EJB事務(wù)環(huán)境中。這很可能是將來許多環(huán)境的模型。正交性的另一個(gè)有趣的變體是面向方面編程(Aspect-OrientedProgramming,AOP),這是XeroxParc的一個(gè)研究項(xiàng)目([KLM+97]與[URL49])。AOP讓你在一個(gè)地方表達(dá)本來會(huì)分散在源碼各處的某種行為。例如,日志消息通常是在源碼各處、通過顯式地調(diào)用某個(gè)日志函數(shù)生成的。通過AOP,你把日志功能正交地實(shí)現(xiàn)到要進(jìn)行日志記錄的代碼中。使用AOP的Java版本,你可以通過編寫aspect、在進(jìn)入類Fred的任何方法時(shí)寫日志消息:aspectTrace{advise*Fred.*(..){staticbefore{Log.write("->Entering"+thisJoinPoint.methodName);}}}如果你把這個(gè)方面編織(weave)進(jìn)你的代碼,就會(huì)生成追蹤消息。否則,你就不會(huì)看到任何消息。不管怎樣,你原來的源碼都沒有變化。編碼每次你編寫代碼,都有降低應(yīng)用正交性的風(fēng)險(xiǎn)。除非你不僅時(shí)刻監(jiān)視你正在做的事情,也時(shí)刻監(jiān)視應(yīng)用的更大語境,否則,你就有可能無意中重復(fù)其他模塊的功能,或是兩次表示已有的知識(shí)。你可以將若干技術(shù)用于維持正交性:l讓你的代碼保持解耦。編寫“羞怯”的代碼——也就是不會(huì)沒有必要地向其他模塊暴露任何事情、也不依賴其他模塊的實(shí)現(xiàn)的模塊。試一試我們將在183頁的“解耦與得墨忒耳法則”中討論的得墨忒耳法則(LawofDemeter)[LH89]。如果你需要改變對(duì)象的狀態(tài),讓這個(gè)對(duì)象替你去做。這樣,你的代碼就會(huì)保持與其他代碼的實(shí)現(xiàn)的隔離,并增加你保持正交的機(jī)會(huì)。l避免使用全局?jǐn)?shù)據(jù)。每當(dāng)你的代碼引用全局?jǐn)?shù)據(jù)時(shí),它都把自己與共享該數(shù)據(jù)的其他組件綁在了一起。即使你只想對(duì)全局?jǐn)?shù)據(jù)進(jìn)行讀取,也可能會(huì)帶來麻煩(例如,如果你突然需要把代碼改為多線程的)。一般而言,如果你把所需的任何語境(context)顯式地傳入模塊,你的代碼就會(huì)更易于理解和維護(hù)。在面向?qū)ο髴?yīng)用中,語境常常作為參數(shù)傳給對(duì)象的構(gòu)造器。換句話說,你可以創(chuàng)建含有語境的結(jié)構(gòu),并傳遞指向這些結(jié)構(gòu)的引用?!对O(shè)計(jì)模式》[GHJV95]一書中的Singleton(單體)模式是確保特定類的對(duì)象只有一個(gè)實(shí)例的一種途徑。許多人把這些singleton對(duì)象用作某種全局變量(特別是在除此而外不支持全局概念的語言中,比如Java)。使用singleton要小心——它們可能造成不必要的關(guān)聯(lián)。l避免編寫相似的函數(shù)。你常常會(huì)遇到看起來全都很像的一組函數(shù)——它們也許在開始和結(jié)束處共享公共的代碼,中間的算法卻各有不同。重復(fù)的代碼是結(jié)構(gòu)問題的一種癥狀。要了解更好的實(shí)現(xiàn),參見《設(shè)計(jì)模式》一書中的Strategy(策略)模式。養(yǎng)成不斷地批判對(duì)待自己的代碼的習(xí)慣。尋找任何重新進(jìn)行組織、以改善其結(jié)構(gòu)和正交性的機(jī)會(huì)。這個(gè)過程叫做重構(gòu)(refactoring),它非常重要,所以我們專門寫了一節(jié)加以討論(見“重構(gòu)”,184頁)測試正交地設(shè)計(jì)和實(shí)現(xiàn)的系統(tǒng)也更易于測試,因?yàn)橄到y(tǒng)的各組件間的交互是形式化的和有限的,更多的系統(tǒng)測試可以在單個(gè)的模塊級(jí)進(jìn)行。這是好消息,因?yàn)榕c集成測試(integrationtesting)相比,模塊級(jí)(或單元)測試要更容易規(guī)定和進(jìn)行得多。事實(shí)上,我們建議讓每個(gè)模塊都擁有自己的、內(nèi)建在代碼中的單元測試,并讓這些測試作為常規(guī)構(gòu)建過程的一部分自動(dòng)運(yùn)行(參見“易于測試的代碼”,189頁)。構(gòu)建單元測試本身是對(duì)正交性的一項(xiàng)有趣測試。要構(gòu)建和鏈接某個(gè)單元測試,都需要什么?只是為了編譯或鏈接某個(gè)測試,你是否就必須把系統(tǒng)其余的很大一部分拽進(jìn)來?如果是這樣,你已經(jīng)發(fā)現(xiàn)了一個(gè)沒有很好地解除與系統(tǒng)其余部分耦合的模塊。修正bug也是評(píng)估整個(gè)系統(tǒng)的正交性的好時(shí)候。當(dāng)你遇到問題時(shí),評(píng)估修正的局部化程度。你是否只改動(dòng)了一個(gè)模塊,或者改動(dòng)分散在整個(gè)系統(tǒng)的各個(gè)地方?當(dāng)你做出改動(dòng)時(shí),它修正了所有問題,還是又神秘地出現(xiàn)了其他問題?這是開始運(yùn)用自動(dòng)化的好機(jī)會(huì)。如果你使用了源碼控制系統(tǒng)(在閱讀了86頁的“源碼控制”之后,你會(huì)使用的),當(dāng)你在測試之后、把代碼簽回(checkthecodeback)時(shí),標(biāo)記所做的bug修正。隨后你可以運(yùn)行月報(bào),分析每個(gè)bug修正所影響的源文件數(shù)目的變化趨勢。文檔也許會(huì)讓人驚訝,正交性也適用于文檔。其坐標(biāo)軸是內(nèi)容和表現(xiàn)形式。對(duì)于真正正交的文檔,你應(yīng)該能顯著地改變外觀,而不用改變內(nèi)容?,F(xiàn)代的字處理器提供了樣式表和宏,能夠?qū)δ阌袔椭▍⒁姟叭际菍憽保?48頁)。認(rèn)同正交性正交性與27頁介紹的DRY原則緊密相關(guān)。運(yùn)用DRY原則,你是在尋求使系統(tǒng)中的重復(fù)降至最??;運(yùn)用正交性原則,你可降低系統(tǒng)的各組件間的相互依賴。這樣說也許有點(diǎn)笨拙,但如果你緊密結(jié)合DRY原則、運(yùn)用正交性原則,你將會(huì)發(fā)現(xiàn)你開發(fā)的系統(tǒng)會(huì)變得更為靈活、更易于理解、并且更易于調(diào)試、測試和維護(hù)。如果你參加了一個(gè)項(xiàng)目,大家都在不顧一切地做出改動(dòng),而每一處改動(dòng)似乎都會(huì)造成別的東西出錯(cuò),回想一下直升機(jī)的噩夢(mèng)。項(xiàng)目很可能沒有進(jìn)行正交的設(shè)計(jì)和編碼。是重構(gòu)的時(shí)候了。另外,如果你是直升機(jī)駕駛員,不要吃魚……相關(guān)內(nèi)容:l重復(fù)的危害,26頁l源碼控制,86頁l按合約設(shè)計(jì),109頁l解耦與得墨忒耳法則,138頁l元程序設(shè)計(jì),144頁l它只是視圖,157頁l重構(gòu),184頁l易于測試的代碼,189頁l邪惡的向?qū)В?98頁l注重實(shí)效的團(tuán)隊(duì),224頁l全都是寫,248頁挑戰(zhàn)l考慮常在Windows系統(tǒng)上見到的面向GUI的大型工具和在shell提示下使用的短小、但卻可以組合的命令行實(shí)用工具。哪一種更為正交,為什么?如果正好按其設(shè)計(jì)用途加以應(yīng)用,哪一種更易于使用?哪一種更易于與其他工具組合、以滿足新的要求?lC++支持多重繼承,而Java允許類實(shí)現(xiàn)多重接口。使用這些設(shè)施對(duì)正交性有何影響?使用多重繼承與使用多重接口的影響是否有不同?使用委托(delegation)與使用繼承之間是否有不同?練習(xí)1.你在編寫一個(gè)叫做Split的類,其用途是把輸入行拆分為字段。下面的兩個(gè)Java類的型構(gòu)(signature)中,哪一個(gè)是更為正交的設(shè)計(jì)?(解答在279頁)classSplit1{publicSplit1(InputStreamReaderrdr){...publicvoidreadNextLine()throwsIOException{...pub
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度股東借款轉(zhuǎn)增注冊(cè)資本及利潤分配調(diào)整合同
- 2025年度電力線路運(yùn)維風(fēng)險(xiǎn)管理與合同
- 2025年度電子產(chǎn)品退貨換貨服務(wù)合同范本
- 二零二五年度航空航天項(xiàng)目三方合同違約責(zé)任說明
- 公共安全應(yīng)急救援預(yù)案制定指南
- 數(shù)據(jù)中心運(yùn)維服務(wù)合同及設(shè)備維護(hù)管理?xiàng)l款
- 中學(xué)生數(shù)學(xué)史故事征文
- 產(chǎn)品采購及供應(yīng)保障協(xié)議合同
- 企業(yè)信息化建設(shè)實(shí)施細(xì)則
- 企業(yè)資源共享合作協(xié)議書
- 泰州職業(yè)技術(shù)學(xué)院單招《英語》考試參考題庫(含答案)
- 《食品衛(wèi)生與安全》課程標(biāo)準(zhǔn)
- 第7課《誰是最可愛的人》公開課一等獎(jiǎng)創(chuàng)新教學(xué)設(shè)計(jì)-2
- 骨盆骨折小講課護(hù)理課件
- 2016-2023年江蘇衛(wèi)生健康職業(yè)學(xué)院高職單招(英語/數(shù)學(xué)/語文)筆試歷年考點(diǎn)試題甄選合集含答案解析
- 渣土車司機(jī)安全培訓(xùn)
- 燃?xì)夤鞠琅嘤?xùn)課件
- 成事的時(shí)間管理
- 江西省2023年高等職業(yè)院校單獨(dú)招生考試-江西電力職業(yè)技術(shù)學(xué)院-樣卷
- 汽油安全技術(shù)說明書(MSDS)
- 眼球摘除患者的護(hù)理病例討論
評(píng)論
0/150
提交評(píng)論