|
犯錯(cuò)誤是最好的學(xué)習(xí)方式。 ──莎倫·德雷珀
背景
我們?yōu)榭蛻籼峁┳稍儯瑒傞_始做了很多敏捷的實(shí)踐,包括:持續(xù)集成、測(cè)試驅(qū)動(dòng)、用戶故事需求分析、迭代開發(fā)等等之后,發(fā)現(xiàn)如果再想深入下去,就會(huì)面臨一些“硬骨頭”:遺留系統(tǒng)和開發(fā)設(shè)計(jì)能力的問題。在一些客戶那里,他們產(chǎn)品有10多年了,但是很少有人新加程序文件,寫代碼習(xí)慣于復(fù)制粘貼,都是在已有的類和函數(shù)上修修補(bǔ)補(bǔ)。團(tuán)隊(duì)缺少追求好代碼的品質(zhì)和能力,到處是大函數(shù),上帝類(超大類,任何功能變化都要修改此類),重復(fù)代碼,混亂邏輯,開發(fā)和維護(hù)成本太高。作為一個(gè)顧問,如何才能從根本解決這樣的開發(fā)設(shè)計(jì)能力問題呢?
在ThoughtWorks,如果招聘了一個(gè)沒有經(jīng)驗(yàn)的開發(fā)人員,會(huì)把他們送到印度的TWU(ThoughtWorks University)培養(yǎng)2-6個(gè)月,OO訓(xùn)練營(yíng)是開發(fā)人員的主要課程之一。它專門用來訓(xùn)練開發(fā)人員如何使用面向?qū)ο螅绾芜M(jìn)行測(cè)試驅(qū)動(dòng)開發(fā)。通過這樣的訓(xùn)練之后,開發(fā)人員可以很快的掌握面向?qū)ο箝_發(fā)和簡(jiǎn)單設(shè)計(jì)能力,養(yǎng)成追求好代碼的品質(zhì)。所以,我們也為客戶的團(tuán)隊(duì)引入了TWU的OO訓(xùn)練營(yíng)活動(dòng)。
活動(dòng)方式
OO訓(xùn)練營(yíng)的培訓(xùn)方式和我們傳統(tǒng)的“填鴨式”教育完全相反。它采用的是蘇格拉底式教學(xué)法,顧問不會(huì)給你任何標(biāo)準(zhǔn)答案,而是通過問題的引導(dǎo),讓學(xué)員自己一步一步找到最佳的解決方案。我的OO訓(xùn)練營(yíng)的組織方式一般是:
一、顧問提出需求
顧問在訓(xùn)練營(yíng)活動(dòng)會(huì)扮演著客戶的角色。活動(dòng)一開始的時(shí)候,會(huì)以客戶的身份提出一個(gè)需求,讓大家去完成。例如:要求建模一個(gè)長(zhǎng)方形。
二、簡(jiǎn)單設(shè)計(jì)
以分組討論方式做一個(gè)簡(jiǎn)單設(shè)計(jì),一般從如下三個(gè)方面進(jìn)行設(shè)計(jì)。
- 類名是什么?
- 類的職責(zé)是什么?對(duì)于復(fù)雜需求,可能會(huì)要求畫一個(gè)簡(jiǎn)潔的UML對(duì)象關(guān)系圖。
- 類的測(cè)試用例會(huì)有哪些,并且找到第一個(gè)測(cè)試用例
討論結(jié)束之后,每組介紹一下各自的討論結(jié)果。通過分組討論和顧問的引導(dǎo),對(duì)建模一個(gè)長(zhǎng)方形需求一般會(huì)得到如下的設(shè)計(jì)結(jié)果:
- 類名是: Rectangle
- 類的職責(zé)是計(jì)算周長(zhǎng)和面積
- 計(jì)算周長(zhǎng)的測(cè)試用例:
a. 正常場(chǎng)景。如果長(zhǎng)是2,寬是3,那么周長(zhǎng)是10
b. 異常場(chǎng)景,寬為0的情況。如果長(zhǎng)是2,寬是0,那么應(yīng)該拋出異常
c. 異常場(chǎng)景,寬為負(fù)數(shù)的情況。如果長(zhǎng)是2,寬是-3,那么應(yīng)該拋出異常
這些測(cè)試用例完全是由學(xué)員自己設(shè)計(jì)出來的,沒有標(biāo)準(zhǔn)答案。作為顧問只是引導(dǎo)大家,讓每組的測(cè)試用例更具體和全面。假設(shè)沒有一組學(xué)員沒有考慮到異常情況的測(cè)試用例,這時(shí)也也不用指出來,等后面代碼展示的時(shí)候,再指出這個(gè)問題,因?yàn)榉稿e(cuò)是最好的學(xué)習(xí)方式。
三、測(cè)試驅(qū)動(dòng)(TDD)開發(fā)
學(xué)員根據(jù)設(shè)計(jì)和討論的結(jié)果,用測(cè)試驅(qū)動(dòng)的方式進(jìn)行結(jié)對(duì)編碼開發(fā)。要求先寫單元測(cè)試,寫完一個(gè)單元測(cè)試之后,運(yùn)行測(cè)試失敗,然后再去寫業(yè)務(wù)代碼。如果沒有失敗的測(cè)試,不允許寫一行業(yè)務(wù)代碼。這樣嚴(yán)格的要求,讓大家養(yǎng)成測(cè)試驅(qū)動(dòng)開發(fā)的好習(xí)慣。
兩個(gè)人一組使用結(jié)對(duì)方式進(jìn)行開發(fā),要求使用乒乓式的結(jié)對(duì)方法。假設(shè)是A和B進(jìn)行結(jié)對(duì)開發(fā),A先寫一個(gè)測(cè)試用例,編譯通過但是測(cè)試失敗。然后把鍵盤和鼠標(biāo)交給B,B寫業(yè)務(wù)代碼,讓測(cè)試通過,然后為A再寫一個(gè)失敗的測(cè)試。通過這種乒乓的方式,一個(gè)人寫測(cè)試用例,一個(gè)人實(shí)現(xiàn)業(yè)務(wù)代碼,并且不停的變換角色。這樣兩個(gè)人可以很好的進(jìn)行配合,互相給對(duì)方出“難題”,一個(gè)人在寫實(shí)現(xiàn)的時(shí)候,另一個(gè)人會(huì)思考下一個(gè)測(cè)試用例會(huì)是什么。
有的時(shí)候,我們會(huì)對(duì)團(tuán)隊(duì)提出一些更高的要求。比如編程過程中不允許使用鼠標(biāo),一切都是快捷鍵操作,這樣能提高開發(fā)的效率。編程過程中不允許使用復(fù)制和粘貼功能,如果是相同的功能或者代碼,第一應(yīng)該考慮到的是如何進(jìn)行功能重用,而不是復(fù)制一遍,這樣可以杜絕這樣錯(cuò)誤的編程習(xí)慣。
四、代碼展示和顧問點(diǎn)評(píng)
寫完代碼之后,每個(gè)人開始展示自己的代碼。這時(shí),顧問就開始從代碼里面尋找代碼懷味道(Bad smell),告訴大家什么樣的是好代碼,什么樣的是壞代碼。壞代碼會(huì)有哪些危害,讓后讓大家重構(gòu)。
例如:在實(shí)現(xiàn)長(zhǎng)方形周長(zhǎng)的時(shí)候,有人實(shí)現(xiàn)了getLength了getWidth方法,把長(zhǎng)方形的長(zhǎng)和寬的數(shù)據(jù)暴露出來了。那么這就是一個(gè)代碼壞味道(Bad smell),顧問會(huì)指出這個(gè)問題,告訴大家面向?qū)ο笞钪匾囊粋€(gè)特征是封裝,不應(yīng)該把數(shù)據(jù)直接暴露出來。因?yàn)閿?shù)據(jù)暴露出來之后,一方面造成數(shù)據(jù)的依賴和耦合,另一方面其它代碼在調(diào)用長(zhǎng)方形這個(gè)對(duì)象的時(shí)候,也許會(huì)拿到長(zhǎng)和寬的數(shù)據(jù)自己進(jìn)行運(yùn)算,這樣就破壞了封裝,對(duì)象之間耦合增大,并且容易產(chǎn)生重復(fù)的代碼。然后提問大家,正確的做法應(yīng)該是什么?
答案應(yīng)該會(huì)是長(zhǎng)方形不應(yīng)該把長(zhǎng)和寬的數(shù)據(jù)暴露出來,所有長(zhǎng)方形相關(guān)的運(yùn)算和邏輯都應(yīng)該在長(zhǎng)方形這個(gè)領(lǐng)域?qū)ο罄锩孢M(jìn)行。這也正是面向?qū)ο笤O(shè)計(jì)的一個(gè)重要原則:Tell don’t ask的體現(xiàn)。通過這樣的引導(dǎo)的方式,帶領(lǐng)大家一步一步找到正確的面向?qū)ο笤O(shè)計(jì)方法和原則,同時(shí)糾正那些錯(cuò)誤的編碼和設(shè)計(jì)習(xí)慣。
每次活動(dòng)就是類似這樣的流程。顧問提出需求,然后是討論和設(shè)計(jì),結(jié)對(duì)開發(fā),最后展示代碼和點(diǎn)評(píng)。一輪結(jié)束之后,顧問又給大家一些新的需求,進(jìn)行第二輪,如此一直的循環(huán)下去。
經(jīng)驗(yàn)和教訓(xùn)
我多次為客戶的團(tuán)隊(duì)組織過類似的訓(xùn)練營(yíng)活動(dòng)。最長(zhǎng)的一次堅(jiān)持了3個(gè)月,每周2次,每次兩個(gè)小時(shí),完成了OO訓(xùn)練營(yíng)的所有課程。堅(jiān)持參加完這個(gè)活動(dòng)之后,開發(fā)人員和技術(shù)Leader的都能真正的全面掌握面向?qū)ο笤O(shè)計(jì)方法和原則,并且把這些用到自己的項(xiàng)目中。下面是我的一些經(jīng)驗(yàn)和教訓(xùn):
- 活動(dòng)時(shí)間安排。如果每次活動(dòng)2個(gè)小時(shí),一般是安排在下班左右,一小時(shí)工作時(shí)間和一小時(shí)個(gè)人時(shí)間。因?yàn)榇蟛糠謭F(tuán)隊(duì)都有交付的任務(wù)和壓力,完全占用工作時(shí)間不現(xiàn)實(shí)。
- 家庭作業(yè)。由于我培訓(xùn)的一些團(tuán)隊(duì)技術(shù)基礎(chǔ)不是很好,大部分開發(fā)人員無法在2個(gè)小時(shí)內(nèi)完成編碼任務(wù)。所以我會(huì)把沒有完成的任務(wù)留作家庭作業(yè),讓大家在活動(dòng)之后完成,這樣在OO訓(xùn)練營(yíng)的時(shí)候主要是進(jìn)行設(shè)計(jì)討論和代碼的展示點(diǎn)評(píng)。這樣也有一個(gè)好處,每個(gè)人都會(huì)有時(shí)間進(jìn)行獨(dú)立思考,對(duì)代碼進(jìn)行重構(gòu)產(chǎn)生一個(gè)更好的設(shè)計(jì)。
- 活動(dòng)整個(gè)過程是以引導(dǎo)為主,讓大家自己進(jìn)行編碼、重構(gòu)和討論,自己解決問題,尋找設(shè)計(jì)的最佳解決方案。顧問只是擔(dān)任一個(gè)類似主持人的角色。
參考資料
如果你也想在自己的團(tuán)隊(duì)或者公司引入這樣的活動(dòng),下面一些資料可供參考:
- OO訓(xùn)練營(yíng)資源
為了做好OO訓(xùn)練營(yíng)這樣的活動(dòng),需要事先設(shè)計(jì)好一些練習(xí)和案例。下面的書籍里面就有一些很不錯(cuò)的例子,可以直接拿來練習(xí)。比如:《測(cè)試驅(qū)動(dòng)開發(fā)》這本書里面的Money例子,《重構(gòu)》這本書的影評(píng)出租店例子等等。也可以去網(wǎng)上找到一些訓(xùn)練營(yíng)的資料,我這里為大家提供一個(gè)咖啡機(jī)的案例,它有現(xiàn)成的用戶故事,可以直接拿來做OO訓(xùn)練營(yíng)。它的網(wǎng)址是:http://agile.csc.ncsu.edu/SEMaterials/tutorials/coffee_maker/index.html 。
- 參考書籍
顧問點(diǎn)評(píng)是一個(gè)很重要的環(huán)節(jié)。要求顧問首先自己是一個(gè)優(yōu)秀的程序員,有豐富的開發(fā)和設(shè)計(jì)經(jīng)驗(yàn),這時(shí)候才能去很好的指導(dǎo)別人。精通和掌握下面幾本書,是對(duì)顧問最基本的要求:
《重構(gòu):改善既有代碼的設(shè)計(jì)》Martin Fowler
《測(cè)試驅(qū)動(dòng)開發(fā)(中文版)》Kent Beck
《敏捷軟件開發(fā):原則、模式與實(shí)踐》Robert C. Martin
《代碼整潔之道》Robert C. Martin
關(guān)于作者
錢安川,ThoughtWorks公司高級(jí)軟件咨詢師、敏捷過程教練、資深講師、Team Leader、開發(fā)者、 BeiJing Open Party組織者和主持人。個(gè)人博客:敏捷開發(fā)訓(xùn)練。
it知識(shí)庫(kù):敏捷咨詢工具箱(二)──OO訓(xùn)練營(yíng),轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。