教學班級地址:BUAA_SE_2021_LR
GitLab項目地址:2021_Yilong_Li-Changyao_Tian_pair_work/task2
成員學號 | |
---|---|
L同學 | 3580 |
T同學 | 3636 |
結對編程感受分享

From T同學
本次結對編程項目基本延續了上一次的分工,L同學主要負責測試,我主要負責實現。由於L同學有實習任務,所以前期不太抽不開身;因此這次的關於軟硬鏈接和用戶組相關的設計也主要是由我來完成。同樣,我們繼續借助在上一篇博客中提到的Code with Me
插件幫助我們完成本次的編程任務。

但這些並非我這次想主要分享的。我更想借這一機會,談談我對於本次結對編程與未來的團隊項目以及將來有可能參與的更大型的企業級項目之間的聯系與個人的一些看法。
MVP還是MBP?
在軟工課上,我們了解到了MVP
與MBP
這兩個概念,MVP即為最小可用產品(Minimal Viable Product),MBP即為最全最美產品(Most Beautiful Product)。那么問題來了,如果說過去我們完成的OO課程作業以及其他編程作業是MBP,需要考慮各種極端情況各種邊界數據;如果說未來我們將要完成的軟工團隊項目是MVP,在最短的時間內完成一個盡可能有用的產品;那么,這次結對編程的定位是什么呢?
MVP還是MBP?
首先,從考核方式來看,結對編程采用的仍然是和OO課程一樣的弱測+強測的線上評測方式,這顯然是MBP的路數。畢竟作為開發者的我們,必須要考慮到指導書中所提到的全部可能的情況,才有可能通過測試;因為沒有誰知道,評測機或者課程組真正想要考察的是哪幾種特殊的情況。於是,issue區里的各路大神也就自然而然地開動腦筋思考形如軟鏈接鏈
這樣的極端樣例出現的可能性了——這顯然不是想和指導書過不去,不是非要在雞蛋里挑骨頭;而是因為大家潛意識里都在把它當成一個MBP的項目去做,為了拿分,在設計時就做到盡善盡美是必不可少的。這個時候,指導書如果有任何不完備的地方,或者總是在反復地改來改去,顯然都會引起同學們的極度不適,造成心理上的額外負擔。
但是,從開發過程來看,如果把它當成一個MBP的項目去做,那心累是一方面,另一方面,這真的就是課程組想看到的結果嗎?因為這樣一來,大家首先要做的就是去死扣指導書的字眼,不放過每一個細節,指導書說什么就是什么;但可能反而會不小心忘了,最自然最合理的實現方式應該是什么樣的,某個地方為什么要這么去做,是指導書的筆誤還是其有意為之?
所以,我更傾向於把結對編程看作是從MBP向MVP的思維上的一種轉變的過程。那么,為了幫助同學們實現這一思維過程的轉變,指導書應該在其中扮演什么樣的角色?是要把每個細節卡的一板一眼,還是盡可能以簡潔的語言表達最終想要做的結果?評測機又應該扮演什么樣的角色?是盯着邊界數據不放,還是強調功能的完備性?
更進一步的,現有的這種傳統的基於測試點的評測方法是否合適?是否要盡可能地額外多考慮一些架構的合理與優美,考慮如何展示同學們每一次迭代的過程?特別地,當指導書本身也有不完美的地方的時候,如何與同學們之間相互協調,實現設計上的向后兼容,我覺得這都是課程組值得作進一步思考的地方。
需求的迭代與實現的升級
不是什么東西都有標准答案的。 —— 某牟姓同學
除了MVP與MBP之爭外,本次結對編程帶給我的另一點很深的感觸就是——我第一次真正意義上感受到了什么是真正的面向現實的開發。
為什么說是面向現實呢?因為現實中,用戶的需求總是在反反復復地更新的,不存在一個所謂的像指導書一樣的絕對權威告訴你這里那里要怎么做,並且信誓旦旦地跟你保證以后都按這個來。
為什么說是第一次呢?因為,以前雖然OO每一單元地每個task都是在上一個task的基礎上進行迭代的,但是奈何人家每個task本身的指導書在開發周期內都是確定不變的。而這次就不一樣了:1周之內,指導書本身的issue就有39個,來來回回共計commit了17次。
我知道這不是課程組有意如此,但其實我還真有點希望課程組有意如此。因為惟其如此,才能真正體現出現實的不完美:如果說數學是這世上最完美的體系,那么物理就是對它最無情的拆穿;如果說程序是這世界上最完美的存在,那么來自客戶的需求就是對這一命題最好的反駁。
那該怎么應對這樣瘋狂的變動呢?
我能想到的比較合適的方法就是設計先行+測試驅動+文檔注釋,畢竟文檔和測試改起來總是要比程序內部的邏輯本身要容易的多;所以,面對如此頻繁的需求變動,最好的辦法應該就是:
- 首先設計出整體上大致的編程框架,明確各個實體的抽象級別;
- 然后再針對頻繁變化的需求,針對那各種
if else
組成的分支,構建相應的測試單元; - 最后,對於程序中的核心方法,給出盡可能詳細的注釋和文檔,便於后期的復審和修正。
三者結合,從而實現整體上效率與正確性的兼顧與平衡。
From L同學
敏捷軟工第二次結對編程給我的感受很差,我從第二次軟工作業學到的唯一知識就是,我沒有從這次結對編程中學到任何知識。
在這里我還想自說自話地給我的舍友們道個歉,學期初我們四人並沒有選擇敏捷軟工,但在聽了第一節課PPT和看到了群里HansXXX的安利后,我最先改選了敏捷軟工,舍友們也陸續選擇了該課程。
在改選的那一刻,我至少是做了一定心理准備的,雖然現在看來這所謂的准備是多么狂妄而可笑啊。我的成績不能保研,也沒有考研的打算,所以我雖然沒有力爭上游的期望與實力,但還是有着踏踏實實完成課程任務與團隊項目的目標。所以我雖然一直聽說敏捷軟工任務量大,但我相信更多的投入會帶來更美妙的產出。
而現在,看着已經連續一周心照不宣地打破了12點熄燈上床鐵律的宿舍,看着凌晨四點依然在地下室結對的舍友,看着為了各種模糊的定義而熱鬧非凡的Issue區……我忍不住問自己:這真的值得嗎?
當然,我沒有資格提出這個問題。在這次的結對編程中,我和隊友T同學的重合時間比第一次結對時更少,因此我們的分工是T同學負責實現、我負責測試,T同學的工作量想必是比我更大的。而如果要說單元測試90%覆蓋率和新增的文件與用戶組管理功能,其實和上一次任務差別並不大。
但有一把懸在每個人頭上的達摩克利斯之劍——強測。
誠然,弱測強測機制是督促課程成員考慮各種特例,提升程序正確性的有效手段;但當強測范圍過於寬泛,需要考慮的特殊情況也急劇增長:這個行為在第一次指導書下是這樣定義的,但第二次指導書修改了描述,但Ubuntu上的效果又是這樣……於是乎,我們時而成為新時代孔乙己,精通指導書上對行為定義的四種寫法;時而成為閱讀理解大師,對“不再贅述”的深層含義如數家珍。
最大的恐懼來源於未知,強測亦如此。事實上很多所考慮的情況並不會考察,測試數據中不會出現這樣的數據點。但在issue區沒有明確回復之前,在沒有看到issue區的這條回復之前,在甚至沒讀懂不再贅述之前,就像是在黑暗里亂竄的小白鼠,怎么找也找不到出口在哪。而這樣是否真的就達到了結對編程培養協作意識,為團隊項目做准備的目的呢?一只小白鼠亂竄和兩只小白鼠亂竄有什么區別呢?面對着不斷修改的需求再好的架構也會變成廢紙吧?
來之前我期待着驚喜,今年羅傑班有改革,能上評測機的地方上評測機,不能上的地方也有安排,沒有體驗是不能否定的,躺平畢業不可取,好好鍛煉才是真。
來之后我意識到了自己的渺小,夢回面向對象的那個夏天,還是熟悉的配方,熟悉的助教頭子,兩門老師不同、培養目標不同的課程竟有如此相似的體驗,妙哉!
當然吐槽了這么多,等到發布指導書3時,我依然會像斯德哥爾摩患者一樣湊上去,一天不寫軟工就像得了戒斷反應,畢竟能不能及格不是我想的算。我對所謂的改革已經沒有什么好說的了,期待之后的團隊作業吧。
另外不管怎樣在Issue區答疑和寫指導書的助教們都辛苦了,非常感謝你們的付出。
以上,這周6天里我有4天在實習,趁着編譯不過的空隙摸魚寫下了這些文字,希望從剛剛開始就不斷在我周圍晃悠的上司沒有看到。
項目設計與實現思路
在第一次作業的基礎上,本次項目由實現了簡單的用戶和用戶組系統,同時增加了對軟硬鏈接和移動復制的支持。前者在本次作業中相對而言實現起來較為容易,后者雖然看似只是增加了幾條指令,但復雜度卻平白無故上升了好幾個數量級。
為了能夠對此有一個更直觀的理解,且看下表(基於IDEA的Calculate Metrics
插件):
method | CogC | ev(G) | iv(G) | v(G) |
---|---|---|---|---|
MyFileSystem.catFile(String) | 1.0 | 2.0 | 1.0 | 2.0 |
MyFileSystem.changeDirectory(String) | 1.0 | 2.0 | 1.0 | 2.0 |
MyFileSystem.changeFileContent(String,String,boolean) | 5.0 | 5.0 | 4.0 | 5.0 |
MyFileSystem.copy(String,String) | 0.0 | 1.0 | 1.0 | 1.0 |
MyFileSystem.fileAppend(String,String) | 0.0 | 1.0 | 1.0 | 1.0 |
MyFileSystem.fileWrite(String,String) | 0.0 | 1.0 | 1.0 | 1.0 |
MyFileSystem.information(String) | 1.0 | 2.0 | 1.0 | 2.0 |
MyFileSystem.linkHard(String,String) | 18.0 | 9.0 | 7.0 | 10.0 |
MyFileSystem.linkSoft(String,String) | 20.0 | 9.0 | 6.0 | 10.0 |
MyFileSystem.list(String) | 1.0 | 2.0 | 1.0 | 2.0 |
MyFileSystem.makeDirectory(String) | 4.0 | 3.0 | 2.0 | 3.0 |
MyFileSystem.makeDirectoryRecursively(String) | 11.0 | 7.0 | 6.0 | 8.0 |
MyFileSystem.move(String,String) | 0.0 | 1.0 | 1.0 | 1.0 |
MyFileSystem.moveOrCopy(String,String,boolean) | 36.0 | 17.0 | 13.0 | 20.0 |
MyFileSystem.MyFileSystem() | 0.0 | 1.0 | 1.0 | 1.0 |
MyFileSystem.readLink(String) | 1.0 | 2.0 | 1.0 | 2.0 |
MyFileSystem.removeFile(String) | 0.0 | 1.0 | 1.0 | 1.0 |
MyFileSystem.removeFileOrDir(String,NodeType) | 4.0 | 4.0 | 2.0 | 4.0 |
MyFileSystem.removeRecursively(String) | 0.0 | 1.0 | 1.0 | 1.0 |
MyFileSystem.touchFile(String) | 6.0 | 4.0 | 3.0 | 4.0 |
Total | 109.0 | 75.0 | 55.0 | 81.0 |
Average | 5.45 | 3.75 | 2.75 | 4.05 |
這其中,MyFileSystem.moveOrCopy(String,String,boolean)
函數可以說是獨樹一幟、傲視群雄,而這還是將原有的move
和copy
中的大部分共性邏輯合並到一起,並且把一些子操作封裝到Node
類中之后的結果。MyFileSystem.linkHard(String,String)
與MyFileSystem.linkSoft(String,String)
也同樣是絲毫不落下風。
這三個方法真的是撐起了整個task2的半邊天。
項目整體的UML圖如下:

可以看到,MyUserSystem
與MyFileSystem
基本上實現了相互解耦,通過SystemInfo
靜態類共享全局的一些基本配置信息。
MyUserSystem
方面,由於是多對多關系,因此為了便於高效查詢,故在全局的user表和group表之外,還給每個user和group分別存儲了其對應的group或user的信息。這樣雖然一方面增加了數據冗余,但另一方面也有效提升了查詢效率。
MyFileSystem
方面,為了支持硬鏈接,這里對上一次的作業做了不小的重構。主要是仿照Linux系統,將文件的一些元數據提煉出來,封裝成了Inode
類。這樣,原先的Node
類只需存儲一個指向Inode
的指針即可;這樣做最大的好處在於可以不用為硬鏈接單獨構建類,而是直接把其當作另一種初始化文件的方式即可。如此一來,整個項目中與硬鏈接直接相關的除了linkHard
方法外,就只剩下一個public MyFile(String name, MyDirectory fatherDir, MyFile srcFile)
的構造方法了——可以說是極大地減少了編程的額外負擔。
至於MySoftLink
,這里其實也是把其當作一種特殊的文件來看,但為了保證其與一般的MyFile
在對外方法上有所區別,還是對其單獨構建了一個類(依然繼承自Node
)加以實現。
此外,在上一次設計的基礎上,本次項目仍然繼續為所有的異常構建了不同的exception
子類,從而保證處理過程中可能發生的所有異常行為均能被頂層有效地捕捉到。最終全部構建的異常子類如下圖所示:

如此可以始終保證項目本身具有較好的可讀性與可維護性。
PSP表格
PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | 90 | 90 |
· Estimate | · 估計這個任務需要多少時間 | 10 | 10 |
Development | 開發 | 1200 | 1300 |
· Analysis | · 需求分析 (包括學習新技術) | 30 | 45 |
· Design Spec | · 生成設計文檔 | 25 | 40 |
· Design Review | · 設計復審 (和同事審核設計文檔) | 60 | 40 |
· Coding Standard | · 代碼規范 (為目前的開發制定合適的規范) | 15 | 15 |
· Design | · 具體設計 | 60 | 60 |
· Coding | · 具體編碼 | 800 | 800 |
· Code Review | · 代碼復審 | 60 | 75 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 300 | 420 |
Reporting | 報告 | 60 | 90 |
· Test Report | · 測試報告 | 10 | 10 |
· Size Measurement | · 計算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后總結, 並提出過程改進計划 | 30 | 30 |
合計 | 1500 | 1745 |