聊聊結對編程 (By Jun Guo)


關於項目

      先講講我們的項目本身。這一次和我組隊的是和我來自同一個學院的華平。

      這個項目來源於實際需要。大家很容易想到這么個場景,在公司里,有若干路電梯,而用戶發出上下樓請求后,調度器就需要調度至少一架電梯來響應用戶的請求。但是,如何調度電梯就成了比較麻煩的問題。

      簡單的調度方法就是我們常見的做法——隨機挑選一架電梯到達用戶所在的位置,然后把用戶運到目的地,中途用戶可以進出電梯(如果電梯運行方向合適的話)。這種做法應付普通時段的請求效率不錯,但對於上下班、吃飯高峰期就不大適合了。比如說,上班時期,絕大多數人都是從樓底乘電梯上行,只有極少數的人會在中間樓層發出上下樓請求。此時,我們就需要一個專門優化的算法來提高電梯的運行效率,從而縮短用戶的平均等待時間。

 

      為了減少我們入手這個項目的時間,一開始秋豐老師就給了個框架。這個框架能很好地再現現實生活中的電梯調度系統,但其設計比較復雜。因此,一開始我和華平也花了較多的時間在看懂這個框架上。

      框架有個很奇怪的設計。它定義了電梯、乘客和調度器的接口,並采用工廠模式等方式來生成具體的對象,這看起來是很合理。但事實上,在讀入測試數據時,框架直接采用具體類的Loader將測試數據反序列化為實際對象,這導致接口完全失去了意義——為了讀入測試數據,你不得不使用特定的具體類,根本不能改動它們,更不能使用繼承自接口的自定義具體類來進行操作(Loader產生的類型已經寫死了)。

      唯一可以換成自定義具體類的只有調度器。但事實上,為了達到更好的調度效果,僅僅依靠調度器是不行的,我們還需要電梯能夠提供更多的輔助功能(比如電梯當前正在響應的請求、接下去還要執行的請求等等),從而為調度器的調度算法提供更好的決策基礎。也許你會argue說,這些東西完全可以記錄在調度器里。但事實上,當一個電梯需要同時響應多個請求時,它可以根據這些請求的目的地,計划一條較好甚至最好的路線。如果把各個電梯所負責的請求都記錄在調度器里,電梯計划路線時就需要從調度器取數據。此外,當電梯完成請求后,又需要將請求從調度器的請求隊列中移除。這會讓類與類之間的耦合性變得非常強。因此,提供一個“增強版”電梯勢在必行。但正如前文所述,繼承電梯接口來實現自己的電梯類是行不通的,能夠做的只有繼承給定的具體電梯類,或者用裝飾器模式把它包起來,從而實現更多功能。經過漫長的argue,到最后我和華平決定用裝飾器解決之。

 

      回到算法上。其實最近我和華平都比較忙,因此能夠構思算法的時間也不多。往年有很多人在博客上提出了各種復雜算法,從動態規划+模擬退火到模式識別都有,看得我們兩人心驚膽戰的。By the way,到這一步結對編程就暫停了,因為我們認為如果兩人都沒成型想法就開始討論,只會越搞越亂,更好的做法應該是兩人都獨立想一些完整算法,之后再做討論和整合。華平使用了個貪心算法——綜合考慮剩余容量和最近電梯之后使用貪心策略挑選電梯,這個算法在對付絕大多數隨機數據時能有非常不錯的效率提高。而我作為極簡主義者,算法更加簡單——每次開門后的5個Tick里,只對在第一個Tick進入電梯的用戶所發出的請求進行響應,對在之后4個Tick進入電梯的用戶所發出的請求全部暫緩。事實上,我采用這個算法的初衷是為了方便編程——只要加一個標記變量就可以完成算法;但后來發現,這個算法的效果還是挺不錯的。對付上班高峰期的數據大概只要400 ticks(相對於原本的1200 ticks),對付下班高峰期的數據大概只要600 ticks(相對於原本的1400 ticks),可以說改進還是比較明顯。其實仔細想一下,也可以大概理解這種做法簡單但卻能達到較好效果的原因——在第一個Tick進入電梯的用戶往往已經在外面等待很久,而且人數比較多;而在后續Tick才進入電梯的用戶是沒有經過等待的,並且人數比較零星,所以我們應該優先響應在第一個Tick進入電梯的用戶所發出的請求。

      最后,我們兩人一起分析各自的算法,互相交流改進,並進行大量測試,把我們各自提出的算法相融合,得到最終算法。雖然不知道對比其他人的效果如何,但自我感覺調度效率還是挺高的。

       

      總的來說,由於這次項目代碼量很少,需要敲代碼的時間遠遠少於理解框架和設計算法的時間,代碼行數也極少(調度算法少於100行),因此結對編程更多地體現為二人討論,並沒有顯示出它能夠提高代碼質量這一優勢。

 

 

結對編程吐槽

      注意,以下內容可能讓結對編程崇拜者感到不適。如果您是結對編程崇拜者,建議現在就關閉此網頁。

 

      在軟件工程開發的各種實踐中,有一種非常特別的開發方式:你有一個Parter,你們兩人坐在一起,面對同一台顯示器,敲打同一個鍵盤,揮舞同一個鼠標;你們一起思考,一起分析,一起編程。對,這就是Kent Beck在《Extreme Programming Explained》一書中介紹的結對編程(Pair Programming, Driver Observer Pattern)。

      這也許是各種實踐中最具爭議的開發方式了。如果有人問你,你喜歡Scrum嗎,你多半會回答,“不怎么清楚,要試試看”;但如果有人問你,你喜歡結對編程嗎,我猜你馬上會給予明確的回復。喜歡它的人會覺得好處多多而且成本不高,不喜歡它的人會覺得討厭得難以想象。喜歡與不喜歡都可以形成強大的陣營,兩邊都不乏重量級的高手。

 

      這一次的項目,按要求我們使用的就是結對編程。很不幸,在大一課程里被強制使用結對編程后,我對結對編程好感度就急劇下降。因此雖然這個項目用了結對編程來開發,但我依舊會吐槽結對編程。下面的吐槽與本次項目無關,僅僅是個人感受。

      結對編程里經常提到,兩個人一起工作,使用電腦的人就不會去聊QQ,不會去刷微博,因此效率會變得更高。但事實上,兩個人一起工作,也可能在一起交談一些與工作無關的事情,比如談下Surface可能的售價,聊聊Lumia 920的無線充電多牛逼等等,這反而分散了注意力,導致效率更為低下。

      雖然結對編程總是宣稱兩個人一起檢查代碼能夠增強代碼和產品質量,並有效的減少BUG,但也有很多人覺得,與其兩個人一起檢查代碼,還不如一人寫代碼一人寫測試。寫測試的人可以完全不管另一個人的代碼實現,從而避免兩個人一起陷入思維誤區,這可能讓代碼質量更高。也許有人會argue說兩個人一起陷入思維誤區的概率有多高,但其實你只要去看下ACM比賽的數據,看看每道題用同一個錯誤算法的人有多少你就知道了。

      另外,結對編程總是假定兩個人能夠和諧工作。但事實上,很多程序猿寫代碼時非常不願意別人在他身后指指點點——無論這個人是大牛還是小白;很多程序猿們也不怎么願意一邊寫代碼一邊和別人說明自己的想法,因為他覺得這降低了自己的編碼效率。我就是這么一例。總的來說,人是一種非常復雜的動物,人的缺點和內心的陰暗面可能會比我們想像的要糟糕得多,而這些東西是可以讓一切事物失敗的。所以,正如《人件》所說,人才是軟件開發中最核心,也是最需要花時間去關注的事情。而結對編程,更傾向於把兩個人看成兩台具有相同先驗的和諧機器。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM