Unit4
一、第四單元作業的架構設計
第四單元個人認為主要是考察對于層次結構的了解,即如何了解并處理好UML圖的樹狀結構組織,在理好層次之間以及層次内部的互相關系之後,就隻剩下代碼實作的問題了。但是不得不說,剛開始接觸UML特别是對starUML不熟悉的時候,如果指導書說的不是太清楚,真的很難動手實作,比如說在第一次作業的時候,我就完全了解錯了,導緻難度飛升,一度懷疑老師所說的“簡單”。雖然說相比JML是真的不太簡單
由于主要架構課程組都已經做好了,是以我們基本不用構思什麼架構,隻需要做好階層化的組織就行了
第13次作業
這次作業隻要求實作類圖相關的存儲與查詢。
在資料的存儲方面,考慮到不同層次的元素之間是有很強的關系的,常常需要進行特定的操作,比如對于
class
這個元素,查詢指令中有查詢與class相關聯的需求以及查詢他所實作的接口的需求,很自然的想到被關聯的class執行個體和被實作的interface執行個體最好作為屬性放進此class執行個體中,當然還需要提供一系列的查詢操作。
因為查詢操作衆多,顯然需要使用
hashmap
進行存儲,剛好
hashmap
是一對一的查詢使用起來非常舒服,這裡如果出現有多個鍵相同但是值不同的話,說明存在異常(同名),再建一個
name2Valid
的
hashmap
,對每次查詢時輸入的鍵進行檢查是否有效就好了,速度也非常快。
但是課程組提供的接口給出的是課程組寫的類對應的執行個體,此時如果采用繼承來做就不太好執行個體化自己寫的類,是以我幹脆把課程組給的執行個體封進了自己寫的類中,雖然結構上有億點醜陋,有一點不OO,但是因為到了烤漆,也就将就着用了,最終形成以下結構:(其他的元素也大都如此,把課程組給的執行個體封進自己寫的類中,然後就能很友善地使用
public class MyClass {
private UmlClass umlClass;
private MyClass father = null;
private MyClass topFather = null;
private boolean updatedTopFather = false;
...
private HashSet<MyInterface> interfaces = new HashSet<>();
private String name;
private ArrayList<MyClass> associations = new ArrayList<>();
public MyClass(UmlClass umlClass) {
this.umlClass = umlClass;
name = umlClass.getName();
}
...
public void setTopFather(MyClass topFather) {
this.topFather = topFather;
}
public MyClass getTopFather() {
return topFather;
}
public void setUpdatedTopFather(boolean updatedTopFather) {
this.updatedTopFather = updatedTopFather;
}
public boolean hasUpdatedTopFather() {
return updatedTopFather;
}
...
public void addOperation(MyOperation operation) {
operations.add(operation);
}
public HashSet<MyInterface> getInterfaces() {
return interfaces;
}
...
}
此外,由于第一次作業CPU使用時間限制為2s,其中有幾個非常耗時的查詢操作,查實作的接口,(這裡還需要查接口的繼承情況),查關聯的類等。
考慮到實作的難度,我選擇用遞歸來解決問題,同時采用記憶化加速。即将相關的操作提供一個
flag
,如果
flag
有效則直接傳回對應的查詢元素,若無效,則進行對應的遞歸查找,并更新相應的資訊的存儲。
異常處理方面,要特别注意異常抛出的順序,大體按照從頂層向底層的順序抛異常就好了。
最後,考慮到輸入元素的順序可能不确定,是以我選擇把所有輸入的元素先分類存儲,然後按照合理的順序一個一個地存進相應的類中,為了友善管理,我寫了一個
MyinfoLoader
的類來專門處理讀入資訊的問題。

第14次作業
第二次作業相比第一次多了時序圖和狀态圖的處理,處理過程完全一樣,有了第一次的經驗,這次作業做起來顯得比較簡單。同時CPU使用時間放寬到10s,再加上多的兩個UML圖的查詢基本上沒有什麼耗時的查詢操作,完全不需要什麼算法,直接用暴力做法硬莽就是了。
由于新增了兩種UML圖,我新增加了兩個子產品進行管理:
第15次作業
這次作業需要增加Rule的check操作,一共八條,可能是因為在烤漆,整體實作難度不太高
Rule3
和
Rule4
要求接口不能重複繼承接口,類不能重複繼承類,類不能重複實作接口。
由于在我的架構中,
MyinfoLoader
在讀取資訊的時候需要将實作的類或者繼承的類(接口)存進屬性,隻需要對于第一次作業資訊讀取存儲的部分加一個判斷,并提供一個
flag
,如果存儲資訊的過程中出現了不合法的行為,那就直接把對應的
flag
置為有效,檢查的時候查對應的
flag
就行了
比如在Rule4的檢查中:
如果沒有update并且父類不為空,這時候更新他的繼承的接口首先需要遞歸地更新他父類的接口,然後檢查一下父類是否合法(如果父類标志位有效,子類置标志位)
再對父類實作的接口做一遍更新,同時如果接口标志位有效(不合法)那子類肯定不合法,置标志位。
最後把所有的接口合在一起去重,看是否有重複的,如果有表明需要置标志位,無效。
在
check
子產品中隻需要對每個類更新一下,然後查一下标志位就好了:
public void checkForUml003() throws UmlRule003Exception {
HashMap<String,MyInterface> id2Interface = myUmlClassModelInteraction.getId2Interface();
Iterator iterator = id2Interface.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String,MyInterface> entry = (Map.Entry)iterator.next();
MyInterface myInterface = entry.getValue();
myUmlClassModelInteraction.updateFatherInterface(myInterface);
}
iterator = id2Interface.entrySet().iterator();
HashSet<UmlInterface> ans = new HashSet<>();
while (iterator.hasNext()) {
Map.Entry<String,MyInterface> entry = (Map.Entry)iterator.next();
MyInterface myInterface = entry.getValue();
if (myInterface.getDup() == true) {
ans.add(myInterface.getUmlInterface());
}
}
if (!ans.isEmpty()) {
throw new UmlRule003Exception(ans);
}
}
Rule2
有裡面關于類和接口是否成環的問題,因為CPU時間十分充裕,直接用暴力做法就好,需要注意的是,對于已經周遊過的點需要打上标記,否則将導緻TLE
關于類的成環問題,由于隻存在單繼承,最簡單的做法就是一直查父類,看是否能回到開始的類,如果能,說明類的繼承會成環
需要注意的是,當所查的類在一個環之外的時候,會出現死循環,是以還得記錄查找路徑,當再次出現記錄的路徑裡的類的時候應該結束查找。
接口成環我采用了bfs如果會回到最初的接口表示成環,注意為查過的點打上
flag
二、四個單元中架構設計以及OO方法的演進
- 第一單元:主要是多項式求導的相關問題,重點在于了解類的繼承以及對于面向對象思維的初步了解,由于剛開始基本沒有面向對象的思維,做出來的架構很差,重構一次之後用二叉樹做出了自認為擴充性很好很棒的架構,但是一到化簡就頭皮發麻了,之後聽到老師說不一定是二叉樹,可以是多叉樹(其實我感覺二叉樹相比多叉樹更具有面向對象味),然後又做了一次重構,達到了比較好的效果。我的基本架構就是不同的函數雖然求導的具體做法不同,但是都可以抽象為項,然後項和多項式之間可以互相套娃,都有求導接口,對頂層求導,會将求導這個方法一層層分發下去,而底層有不同的具體實作。
- 第二單元: 主要是關于多線程的了解,關于多線程代碼的編寫與調試,關于生産者消費者以及其他部分設計模式的了解。在第一次寫的時候由于沒有考慮清楚細節,對于死鎖的認識也不清晰,遇到了各種各樣的bug,一團亂麻,再加上對于多線程debug也不熟悉,幹脆推了重寫,由于第一次作業架構還可以,後兩次作業就直接把前面的架構包一下就能直接用了。總的來說就是需要分層次,該電梯管的就電梯管,該排程器管的就排程器管,不要有“長臂現象”,否則容易造成死鎖,并且效率損失嚴重!
- 第三單元:主要是關于JML語言的了解與實作,把規格這個概念建立在我們腦海中。雖然寫起來一長串一長串的(課程組寫ಥ_ಥ )但是對于了解确實很友好,不會産生二義性。本單元是真的可以說沒有任何架構,把指導書看好然後對着課程組的代碼實作相關的函數就完事了,唯一需要注意的就是方法的複雜的,是以會涉及到一些算法的問題。
- 第四單元:主要是關于UML圖的了解,訓練的是階層化子產品化的能力。首先需要通過starUML了解好作業中的UML圖(類圖,時序圖,狀态圖)的相關概念與樹狀結構,然後自己構造相應的類提供相應的存儲與查找方法進行查詢。相比第三單元多了些架構設計,但沒有多很多,做好階層化管理就好。
三、四個單元中測試了解
- 第一單元:第一次自己動手寫評測機,對于個方面都不太熟悉,加上第一單元我并不是很會遞歸下降,不僅作業中讀取很費力,評測機的資料生成也很費力,即使勉勉強強生成了資料強度也還是不高,導緻第一單元作業中有很多bug淚目,具體來說我的做法就是通過正規表達式解析逆向生成資料,然後跑出結果,然後與正确結果對拍,正确結果來自
裡的Python
sympy
……掌握了評測機的主要搭建思路:
(構造資料->用指令行跑代碼并記錄結果->與其他結果對拍并記錄對拍結果)為後面的評測機書寫打好了基礎。
- 第二單元:在
中構造電梯類,生成資料之後,跑代碼,每輸出一個提示資訊,就讀取并解析,更新Python
中電梯類的相關狀态并檢查,一旦不合規範就相應的錯誤Python
- 第三單元:随機生成各種資料(強度可能不高,用數量保證品質),然後找幾個同學一起跑并對拍;專門構造一些容易逾時的針對性資料,用
庫來測運作時間,精度很高time
- 第四單元:先自己構造各種邊界資料,考慮到一些基本情況,進行手動人工測試,確定程式大緻正确。然後手工生成針對性資料,多個同學一起跑并對拍。
四、課程收獲
一學期的OO課終于結束啦!總結了一下,大概有一下收獲:
- 面向對象思想的初步建立,無論什麼好思想都是深邃的,思想的建立不可能一蹴而就,面向對象程式設計思想的初步建立将自己領進了這個領域的大門,為之後自己的探索指了一條明路
- 碼量的飛躍,一學期算下來作業(含重構)加評測機大概也有6000~7000行了吧
- Java語言的熟悉,想要熟練掌握一門語言最好的方式就是多用它編寫代碼。此外踩了許多奇怪的坑,積累了經驗
- 評測機編寫能力大大提升
- 學會了許多工具的使用,特别是git,對于碼量較大的項目是必不可少的東西,以及IDEA裡各種大幅提升體驗的插件
- 了解并掌握了相關的設計模式
- 建立了相關概念,例如:多線程,JML,UML
- 磨砺了心智
五、具體改進建議
- 實驗課最好能公布答案,不然自己根本不知道做對沒,上機幾乎等于無效上機,實在想不到不公布答案的理由
- 指導書中某些寫的不清楚的地方希望能寫的更清楚一點,特别是第四單元,看完指導書真的是一頭霧水
- 希望在每個單元結束後能展示一部分優秀代碼,因為真的覺得自己有的地方寫得很醜陋
- 希望在互測中刀人之後能刀中了誰的回報,這樣就不會有漏網之魚了,同時也能減少惡意Hack的情況
- 希望研讨課的品質能更高一些,最好定幾個ddl,做完PPT後助教要進行稽核,對于品質不合格的打回重做,仍舊不合格将取消本次研讨分享資格
- 讨論開通搜尋功能,并開通标記功能(對于有的文章以及看過了,可以标記為看過,避免浪費時間)