高級四則運算器—結對項目反思(193 & 105)
本周我和一位韓國同學(71061105)一起結對編程完成了我們的結對項目——高級的小學四則運算題目生成器。
PSP表格
PSP2.1 |
Personal Software Process Stages |
Time |
Planning |
計劃 |
|
· Estimate |
· 估計這個任務需要多少時間 |
1.5h |
Development |
開發 |
|
· Analysis |
· 需求分析 (包括學習新技術) |
3h |
· Design Spec |
· 生成設計文檔 |
5h |
· Design Review |
· 設計復審 (和同事審核設計文檔) |
1h |
· Coding Standard |
· 代碼規范 (為目前的開發制定合適的規范) |
2h (設計的時間太短,后面弊端就顯現了) |
· Design |
· 具體設計 |
算法 2h 界面樣式 3h |
· Coding |
· 具體編碼 |
界面生成 6h 算法完成 8h xml讀寫 2h 封裝dll 2~3h 拼接組合 2h 調試 2~3h |
· Code Review |
· 代碼復審 |
4h |
· Test |
· 測試(自我測試,修改代碼,提交修改) |
5h |
Reporting |
報告 |
|
· Test Report |
· 測試報告 |
TuT還沒有 |
· Size Measurement |
· 計算工作量 |
0.5h |
· Postmortem & Process Improvement Plan |
· 事后總結, 并提出過程改進計劃 |
5h (包括了博客) |
合計:54h |
結對編程
結對編程現場證據
第一張展示的是我通過某企鵝廠商提供的討論組共享畫面的功能,在為鐘煥兄講解相關的測試舉例,代碼功能等。
在這個討論里,我為他進行了具體的分工與分割,決定讓他從閱讀源代碼和測試開始進行c#方面的知識學習,并且讓他提出代碼閱讀中遇到的幾個問題。
這一張圖里鐘煥兄在向我匯報他的測試進度以及當前的代碼覆蓋率的情況,并且向我請教了一些測試用例的寫法。
考慮了一下自己的美貌程度,所以鄭重地把我們結對編程的照片放在最后。圖為我在教鐘煥兄使用Git。
在此重新說明一下我們的特殊情況,之前的隊友我跟他多次聯系無果,在國慶前聯系到后,一起討論了一下,最后讓他做了一些測試,他也只做了一個函數的單元測試...并且用了四天...其他時間基本失去聯系,所以我決定單方面切斷與他的組員系。真的是太失望了。
因為我國慶回家的特殊關系,這位韓國隊友玉鐘煥是我在9.29號跟老師說明后爭取到的新隊友,所以實際上我們真正意義上面基的時間只有一天。其他時間基本上都是要么是我給他做出示例(通過某平臺),要不就是我碼代碼的過程中對代碼進行非常詳細的注釋。所以沒能真正地結對編程,所以我將再進行一周的結對編程,完成 沉默的代碼 老師在項目下的評論到的所有功能。并且使用Git提交,到時候會來完善博文。
結對編程優點和缺點
因為實際上面基的時間只有一天,我也沒有真正體會過真人坐在我身邊,我作為領航員的驕傲,以及兩個人能夠思想交流對撞產生火花的激情。只能說是在9.30那天,我在旁一步一步告訴了他算法的整個設計思路,為他指明了代碼構造的大方向——其實沒什么用,因為算法還是我一個人寫的——但是這樣他至少能學一些東西吧,我一直這么想。我還花了一些時間教了他關于Git提交的相關事宜,又因為害怕他看不懂,我為算法的部分基本上做到了一行代碼一行注釋的水準,并且每晚都通過某平臺進行代碼講解。一個人碼代碼,一個在旁code revivew的情景很令人心醉,但是不得不承認的是,在幫助鐘煥兄的過程中我對結對編程確實沒有什么太深的感觸。
但是由于之前跟何某濤有一次一起調操作系統實驗bug、和何某松一起寫算法設計作業的時候算是結對編程,我還是能大致地說說自己對結對編程的優點和缺點的看法。
結對編程的優點是非常顯著的,首先就是高效與低錯。這一點在跟何某松同學一起調試算法作業的時候體會到過,上學期的算法設計作業實際上是一個學期的作業,而就在那一天我們倆用了不到六小時的時間結對完成了它...幾乎沒有怎么調試,很多低能錯誤——我指的當然不是說括號匹配出錯這種編譯錯誤——都被很好地避免了。同時由于兩個人一個人碼,一個人代碼復審,所以實際上對于我們來說每一行代碼都是經過雪亮的兩雙眼睛目測過的。而兩方同時犯錯的概率比一個人要小一些——如果其中有高手的話會小很多。這是結對編程的一大優點——隨時的代碼復審提高了程序邏輯完全正確的概率。
而且由于兩人有互相督促,所以誰都不會聊聊某鵝,刷刷某乎,發發某博,看看娛樂新聞什么的。大塊大塊的時間都可以被用來集中注意力于編程這一純粹的事情上,從而減少了從沒有狀態到進入狀態的轉換過程,很好地減少了時間的浪費。我認為這是結對編程很重要的一點貢獻——他人的監管轉換為高效的執行力。
最后呢,結對編程還是一次思想對撞之旅,每個人都能從對方身上學到些什么——即使他可能編碼能力上不如你。這同時也是一次算法優化之旅,在代碼寫的過程中就可以做一些自己之前并不知道但是結對伙伴比較熟悉的優化,通過這種不斷的交流、學習,兩個人的學識都能得到豐富。并且兩個人的團隊合作意識也會大大提升。正如古語所說三人行,必有我師。二人行,亦能學到許多。
結對編程的缺點我目前還沒體會過,我覺得應該是集中于兩個人的差異上:包括個人衛生習慣,編程習慣,心態等。比如有些同學在別人看他代碼時就是寫不了代碼...我覺得吧,結對編程最怕的是沒能遇上志同道合的人。如果有志同道合的人一起結對編程,那確實是高效的,是可行的,是思想的碰撞。但是如果沒有志同道合的隊友和自己一起結對編程,那和隊友一起編程可能效率還不如自己編程效率高。結對編程不怕隊友能力不夠,只怕隊友態度不行。
所以在此我想建議一下羅老師,能不能不要再使用隨機結對編程的做法呢?據我了解,雖然我因為我自己的原因沒能體驗結對編程,但是很多人也一樣沒有!一部分同學基本上是一個人悶在宿舍不聲不響地完成了所有的任務,另一部分同學雖然是分工完成的,但是基本上都是團隊編程的模式,不是結對編程的模式。:( 您可以去調查一下的!
希望您能讓大家按自主意愿自行組隊,正如我所說的,結對編程志同道合很重要!您可以按照個人項目前百分之五十必須找后百分之五十的同學或者其他比較公平的分法,但是我非常希望能夠有個人意愿為主導的結對編程團隊!
結對伙伴的優點和缺點
由于沒有真正地結對(希望開學后能親自教一下韓國同學:D),對結對伙伴的衛生習慣并不了解(XD),以下列一下他的一些優點吧:
1、很積極。派發給他的任務他很快地就答應了,并且在讓他遠程觀看我碼代碼的時候,他也很積極地參與。
2、他很理解了他所欠缺的地方。在我把任務細分下去派發的過程中,鐘煥兄對一些他覺得不清楚的地方有所疑問,并且他只要有人引他入門,他能一直學習那個東西到學會為止。
3、他比較負責。由于我的VS版本在裝那個代碼覆蓋率插件的時候裝不上...而且由于家里的網十分受限(可能是運營商的緣故,大部分英文網站無法訪問),所以安裝VS的擴展工具時失敗了,所以我這里沒法測試單元覆蓋率。而鐘煥兄的VS是可以測試的,他每次都想讓測試模塊的覆蓋率達到百分之百(其實我原先是想偷懶90%就算的),后來,有兩個模塊在他那里測試確實100%了。
當然他也有缺點,有個比較嚴重的缺點就是:不喜歡主動提問。
不知道為啥,我在下派一些很小的任務讓鐘煥兄去完成的時候,他有一些看不懂的源代碼——即使我寫了注釋他也沒有及時地問我。
只是大概籠統地說一下注釋看懂了,但是代碼有些沒看懂,這一點讓我比較費解——可能是他比較害羞?
一些原則與設計
在尋找與理解這些原則和設計的過程中,我越來越發現這些設計和原則和上學期被大家一致吐槽的面向對象課程中講到的面向對象的幾大原則有很多的相似之處。
信息隱藏(Information Hiding) 原則
維基里提到了,有些人把信息隱藏
和封裝
看作是一種原則:一個軟件通過,通過把信息封裝到一個只提供接口的模塊或結構中來實現信息隱藏。而這實際上就是我們面向對象課里所學到的關于封裝的概念。
在面向對象編程中,封裝可以通過把代碼對預期不確定的實現的依賴轉移到良好定義的接口上來,從而減少軟件開發的風險。
封裝也能保護模塊內部狀態的有效性,防止用戶在手動初始化模塊屬性的時候直接就把模塊的數據設置錯誤了。防止模塊“生下來就是個錯誤”,那以后豈不是大錯特錯了...
封裝的另一大好處就是可以通過限制軟件各個部分之間的相互依賴關系從而減少系統的復雜性,增加系統的魯棒性。
其實封裝的概念在我們平時見得也很多了。像大部分的產品說明書,那都是一個個設計文檔啊。比如一個小小的插座,產品說明書告訴你千萬不要用手伸進去觸摸插座
。但是它并不告訴你“不能用手觸摸插座”的原因是零線火線,高壓中電這種物理上的知識:因為它面對的不是物理學家,而是普通民眾。
As can be seen by this example, information hiding provides
flexibility. This flexibility allows a programmer to
modify functionality of a computer program during normal
evolution as the computer program is changed to better
fit the needs of users. When a computer program is
well designed decomposing the source code solution into
modules using the principle of information hiding, evolutionary
changes are much easier because the changes
typically are local rather than global changes.
這就像我所理解的信息隱藏的功能一樣,千萬不要把使用你這個類的使用者當成一個極其高深的內行人一樣——“我跟你講啊,這兩種算法的實現效率挺低的,你要不還是自己根據這倆算法改進一個?”
而是要把使用這個類的人當作是白癡客戶
——你給出選項讓他們選,而不是讓他們自行創造選項——“選擇算法1還是2?我只有倆選項”
而且我覺得封裝本身實際上只是一種思想——讓別人更容易使用,而信息隱藏可能包含的東西更多。包括隱藏不必要的實現細節,隱藏一些屬性不能讓人隨意篡改——否則后面調試Bug的時候就可能會有這樣的情況:加班調bug竟是因為別人亂改值而導致的。
這種信息隱藏的做法用于我們本次計算模塊的設計是很好的,就是要設計一個公開的類,從這個public
的類中只能使用它已經實現的特定的某幾個功能,其他的組合功能一概不能使用——因為能夠組合成那些新功能的類或實現已經被隱藏起來了。
于是我在設計中就使用了一個Configure
類來作為中間層聯系封裝好的計算模塊與界面模塊。
接口設計(Interface Design)
接口設計其實本身我之前早已有所耳聞。接口設計的初衷當然是為了能夠減少邏輯bug、確立正確的整體框架。如果接口的設計是良好的,那么代碼重構的風險將大大降低——就不會出現什么代碼碼到一半,發現需求理解錯了,需要重構的做法。在設計階段好好地理解需求與設計接口,對于代碼整體的框架,代碼是否能行云流水地寫成是有指導意義的。
好的接口設計包括以下六種原則:
- 單一職責原則
- 里氏替換原則
- 依賴倒置原則
- 接口隔離原則
- 迪米特法則
- 開閉原則
接口設計實際上強調的重點就在于用抽象構建框架,用實現擴展細節。(這段精辟的總結引用自http://blog.csdn.net/zhengzhb/article/details/7296944)
"對擴展開放,對修改關閉"也是這幾種良好設計的核心思想,包括后面要提到的松耦合,契約式設計的做法,目標都是一致的:建立一個相對穩定的可擴展性強的抽象框架,并且可以做到需求的部分改動與增加不會對整個設計框架產生影響。
本次結對項目里我本來也是設計了兩個接口作為檢查類和生成類所要遵循的方法規范,后來發現良好的接口設計真是不容易啊。因為在最后我的兩個類的方法,居然沒有一個是與接口設計中的方法功能一樣的以至于最后被迫舍棄了接口。這可能是因為新手上路,設計的時間尚淺,初期設計的接口是非常不合理的。方法的完善是在代碼一點一點出現矛盾后,一一修改接口,重新設想才完成的。在這過程中接口被迫為更好的設計作出了讓步。但是實際上即使最后我完全沒有用上接口所定義的那些方法,但是總體框架與初步的設計是差不多的。
我最后發現:設計接口是個需要費時間的設計活,需要經過反復考慮。在設計時,即使最后沒能設計得盡善盡美,但對于整體框架,類的交互的設計走向也是有指導意義的。
松耦合(loose coupling)
剛剛提到了接口設計的一大原則叫:迪米特法則,實際上就是松耦合原則。這個原則是由下面的問題引出的:類與類之間的關系越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。
一言以蔽之,本來就不直接聯系的,就不要讓他們發生聯系。
我拿身邊的例子來舉吧,比如國家有某文件要下報,要求各個市級單位的最高領導人全部知曉。政府部門當然是不能去每個市里找各個市的市長去直接送達的,否則國務院有多少總理秘書都不夠用啊...所以要一級一級地下發,政府到省級行政單位,省級再到市級單位。如果說國家跨過了省,承擔了通知一半的市長的責任,那么這一半市長只要有人換屆,國家就會收到通知。這樣市長的頻繁變更對國家的影響就會很大,這是不應該出現的。(也不知道理解對不對哈...)
這次我的設計里有地方就違反了這一原則。直到和鳴神的隊伍進行結對編程后我才發現。我的設計中計算模塊也接管了部分生成到文件的職能,這樣相當于本來是前端很輕松就可以管理的東西,計算模塊“越俎代庖”,這樣只要想對文件有關修改一部分內容,就必須改計算模塊,真是設計上的缺陷啊...
契約式設計(Design by Contract,Code Contract )
契約式設計的提出,主要基于軟件可靠性方面的考慮。可靠性包括正確性和健壯性,正確性指軟件按照需求規格執行的能力,健壯性指軟件對需求規格中未聲明狀況的處理能力。健壯性主要與異常處理機制相關 。
引自http://www.cnblogs.com/riccc/archive/2007/11/28/design-by-contract-dbc.html
契約式設計要求提供一套機制,在客戶程序與提供者之間明確的聲明雙方的職責與權利,即契約,并能夠對這些契約進行驗證。
契約式的設計身為提供者會覺得很煩...確實很煩,尤其對于設計者來說。而且上學期上面向對象課的時候不知道寫了多少前置條件,后置條件,概覽(overview),以及證明。
也只有自己用的時候才知道其好處多多啊...像在這一點上VS的庫函數就很好。我在本次結對項目中,想捕獲Stack
棧結構如果在為棧空時還執行操作Pop
時的異常。于是右鍵 轉到函數定義,就會發現一段和藹可親的 異常拋出
列表,調用者不需要觀察細節就可以方便地知道可能會發生的事。這種契約機制是一本萬利的,即辛苦一時,造福很多世的工作。
關于單元測試
其實...說句實話,我并沒有按照羅老師的要求來一步一步做。我在做完很小的部分后沒有停留,再做一部分功能,等做到預期的某個大功能已經封裝好后,再進行單元測試。并且每次做完每個模塊后,我都要把模塊的測試部分劃分詳細,給出一定的示例測試,然后交給了鐘煥兄來做。但實際上,在他寫測試之前我基本上都測試過,基本是確認了正確之后才寫下一步。
但是我的VS有點問題—集成不了代碼覆蓋率插件。而家里的網受限嚴重沒法安裝,希望能寬限一天,到校后我會及時補充上代碼覆蓋率的圖。
(現在已經補充)因為封裝不太對...所以Core和CheckAnswer兩個類的覆蓋率都比較低,因為讀寫文件的那個測試不管怎么放文件都一直報錯說找不到文件...我也非常奇怪,正在努力解決這個問題。
UML類圖
UML類圖是由VS自動生成的,感覺這圖沒啥用啊...
算法核心
本次項目有關的經驗我全部寫在了這篇博文中:http://www.cnblogs.com/SivilTaram/p/4857271.html
這篇博文現在還極度不成熟,希望一周后的我能讓它變得更加充實:D。
文章列表