1. 程式人生 > >OO博客作業2:第5-7周作業總結

OO博客作業2:第5-7周作業總結

不但 如果 程序 RM 發的 hang eva 場景 summary

(1)從多線程的協同和同步控制方面,分析和總結自己三次作業來的設計策略及其變化。

第5次作業:多線程電梯

基本照搬了課件上“生產者-消費者”模型的設計策略,將InputHandler設計為生產者線程,將Scheduler設計為消費者線程,將RequestQueue設計為托盤。生產者與消費者的工作並發,提高效率。同時,每部電梯設計為一個線程,因為每部電梯的運行彼此不幹擾。InputHandler, Scheduler由主線程創建,三部電梯由Scheduler負責創建,這樣使得調度器可以獲取電梯的狀態。但這種策略並不是最優的。

第6次作業:文件監控

這大概是寫得最失敗的一次作業。

按課件方法構建了線程安全的SafeFile類。實現了同步化的訪問文件狀態、寫文件方法。然而對於每個record型請求單獨建一個文件寫入,違背了訓練初衷。

每個請求設計一個“監控器”線程,直接查看對應文件的狀態,與指導書的設計思路差異較大。這使得程序難以應對path-changed等大規模變化。

總的來說,這次多線程之間沒什麽協同可言。同步控制也僅限於對文件狀態的訪問。

第7次作業:出租車調度

這次作業的設計策略基本照搬第5次作業。輸入處理線程為生產者,調度器線程為消費者,請求隊列為托盤。每輛出租車為一個線程,依然由調度器創建。

寫了線程安全的SafeFile類用於輸出文本文件。乘客請求、參與搶單的出租車信息由調度器負責寫入,出租車運行情況由出租車線程負責寫入,實現了線程協同。

(2)基於度量來分析自己的程序結構

1)OO程序代碼度量

第5次作業:

類名 屬性個數 方法個數 代碼規模 main / run方法規模 點評
Elevate_5 0 1 27 17
Elevator 14 7 176 40 電梯控制邏輯復雜。
Floors 2 4 24
InputHandler 10 6 158 15 寫得太隨意,很多屬性更適合作為局部變量。
Request 4 59
-CarryRequest 1 3
-FloorRequest 1 3
RequestQueue 6 4 56
Scheduler 7 8 209 40 保留了上次的調度器代碼。
Tray 3 4 76

第6次作業:

類名 屬性個數 方法個數 代碼規模 main / run方法規模 點評
Detail 4 3 64
FileState 4 3 26
InputHandler 9 6 107 相似的問題
Monitor 8 2 60 30
SafeFile 2 15 110 為測試者寫了較多的方法
Summary 4 3 64
TestDrive 0 1 15
TestThread
Trigger 3 8 94 設計有問題

第7次作業:

類名 屬性個數 方法個數 代碼規模 main / run方法規模 點評
_Point 2 7 40
CHandler 9 4 76 32
CityMap 4 4 114 算法代碼比較長
FHandler 2 4 55
Queue 3 3 49
Request 3 3 24
SafeFile 2 5 57
Schedule 6 6 146 44 調度方法夾雜輸出代碼
Taxi 16 9 204 40 運行方法夾雜大量輸出代碼
TestDrive 0 1 18 12

3)各次作業的類圖

技術分享圖片

第5次作業:由於指導書給出了上次電梯的參考類圖,自己的設計基本遵循了指導書。但是Tray這個類沒能發揮出應有的作用,只是單純地緩存請求。更好的做法是同時緩存電梯狀態,電梯運行時更新,調度器需要時讀取。

技術分享圖片

第6次作業:寫得很糟糕,有“面條代碼”的嫌疑。對Monitor和Trigger兩個類的職責沒有明確,而且Summary和Detail的代碼有大量重用。

技術分享圖片

第7次作業:很多地方參考借鑒了第5次作業(多線程電梯)。各個類的職責相對清晰。

4)UML的協作圖(從上到下依次是第5,6,7次作業的圖)

技術分享圖片

5)設計原則檢查(僅針對第7次作業)

DIP (Dependency Inversion Principle):高層次不依賴低層次。

程序有兩個渠道獲取輸入,一是通過文本文件獲取城市地圖,二是通過控制臺獲取叫車請求。我的程序沒有將這兩種途徑進行抽象和歸納,導致程序對輸入方式變化的適應性不好。

命名:

在為類、實例變量命名時我盡可能遵循“顧名思義”,但是走向了另一個極端。我將一個記錄點信息的類命名為“Point”,結果後來發現有java.awt.Point這個類,使用GUI時很不方便。無奈之下我將自己寫的類改名為“_Point”,花了一部分時間改名字。在顧名思義的同時,也盡量不要取過於簡單、大眾化的名字,否則容易與JAVA類庫重名,造成麻煩。

另外,eclipse建議包名首字母小寫,類名首字母大寫,作為初學者還是遵守得好。

(3)分析自己程序的bug

第5次作業:

所有的“正確請求測試多線程功能”樣例均有錯(公測未發現或是被發現bug)。

問題所在的類是Scheduler。

被發現了一個CRASH是由於想向文件寫入結果,但文件已經關閉導致的。這暴露出我對於多線程同步控制尚未掌握,且自己構造的測試樣例太寬松,很容易露出破綻。

從設計結構角度分析,我把所有的電梯調度方法全寫入一個調度器類。Scheduler類的代碼規模更是破了200行,遠遠超過其它類。

在總結課上,我才了解到有更加均衡的實現方式。總調度器只負責將請求分配給不同的電梯,而每部電梯對應一個專門的調度器負責排請求的執行先後順序。這種方式顯然更合適,既是遵循均衡原則,又符合實際的應用場景。

第6次作業:

對方沒有進行公測,且沒有發現bug,故不作分析。

第7次作業:

被發現了一個bug,為新增節點“等待狀態下出租車的運行不具備隨機性”。

問題所在的類為Taxi,方法為run_edge

這個bug被發現在情理之中,因為我曾經嘗試過實現出租車的隨機運行,但是失敗了(出租車發生了瞬移)。最終改成了一版確定性運行的方法。

(4)分析自己發現別人程序bug所采用的策略

很多學生(包括我)在編寫多線程程序時遇到的一個技術難點就是如何讓程序正常結束(在eclipse下是在控制臺上方看到<terminated>)。盡管這不是指導書的硬性要求,但是能夠正常結束的程序勢必帶來更好的用戶體驗和更強的魯棒性。如果被測者沒有在readme中對程序的結束進行說明,或是說明能結束但實際上沒實現的,則可以報告bug。

邊界條件是在編程過程中需要著重考慮的。一些在實際情況中完全有可能出現,但編程者很難考慮到的測試點上往往容易發現bug。第7次作業我通過構造“乘客請求起始坐標正好是出租車所在位置”的樣例,成功發現了別人的bug。

後期的作業不但註重程序正確性,而且對程序設計原則也提出了要求。測試者可以閱讀被測代碼,發現其中不符合設計要求的部分,予以扣分。

(5)心得體會

1)線程安全

在線程安全方面,重點是把握課上重點講過的“生產者-消費者”模型,學會將共享對象的存取方法設置為同步化的,以及方法內部具體的寫法。拿到指導書之後,在分析程序需要哪些對象、對象之間關系時,必須額外考慮哪些對象需要被共享的問題。無論是自己寫被共享類的代碼,還是包裝為線程安全類,都要想清楚哪些方法需要保證原子性,並使用synchronized關鍵字。

2)設計原則

從第7講開始,課後作業將程序設計原則也納入了作業要求和互評範圍。設計原則與程序的正確性沒有直接的關系,而是為了讓程序具有較好的可讀性、可擴展性。在軟件開發行業,用戶需求發生變更是經常遇到的事件。遵循課上講過的一些設計原則可以增強程序的可擴展性。在需求變更時,只需要對原有程序進行有限的修改,而不是推倒重來。自己在3次電梯作業中,後面的作業往往不能有效利用前面作業的代碼,原因在於自己沒有在一開始構造一個良好的設計,沒有遵循設計原則。另一些原則是為了程序可讀性,因為將來我們開發的程序只是一個大項目的一部分。不但要保證別人放心調用,而且最好在代碼中清楚寫出自己的邏輯。可能經過後面幾次作業的訓練,我能夠更深入地理解設計原則。

OO博客作業2:第5-7周作業總結