OO第四單元部落格
有知有覺地來到了學期末,也意味著oo課程的學習將告一段落(沒有理論考試必須好評!),但這只是旅途中的一站,在此經歷的,都將伴隨著一道前行,所有過往,皆成序章,oo只是一個起點,啟蒙了架構設計方面的知識,後續還需潛心學習,爭取更上一層樓。
一、總結第四單元的架構設計
這單元的主要困難在於指導書的理解和與IDE的鬥智鬥勇,其實也可以理解,畢竟語言的二義性是客觀存在的,很難實現一個十分嚴謹的表述,雖然理解過程還是十分痛苦就是了。
架構方面,總體來說,就是瘋狂改程式碼使符合checkstyle建立一個模型將輸入的資料進行構造,這主要是通過java中功能強大的圖機制來實現的。
輸入元素的順序問題
由於元素的輸入並非按照邏輯順序,將可能出現已經輸入的元素中找不到對應的父類而出現喜聞樂見的NPE報錯,需要先讀入所有輸入的元素,再按照邏輯順序集中處理,下以類圖的處理為例進行說明。
private HashSet<UmlElement> associations;
private HashSet<UmlElement> associationEnds;
private HashSet<UmlElement> attributes;
private HashSet<UmlElement> classes;
private HashSet<UmlElement> interfaces;
private HashSet<UmlElement> interfaceRealizations;
private HashSet<UmlElement> operations;
private HashSet<UmlElement> parameters;
private HashSet<UmlElement> generalizations;
public void sendInElement(UmlElement element) {
if (element instanceof UmlAssociation) {
associations.add(element);
} else if (element instanceof UmlAssociationEnd) {
associationEnds.add(element);
} else if (element instanceof UmlAttribute) {
if (element.getName() == null) {
wrong005 = true;
}
attributes.add(element);
} else if (element instanceof UmlClass) {
if (element.getName() == null) { wrong005 = true; }
classes.add(element);
} else if (element instanceof UmlInterface) {
if (element.getName() == null) { wrong005 = true; }
interfaces.add(element);
} else if (element instanceof UmlInterfaceRealization) {
interfaceRealizations.add(element);
} else if (element instanceof UmlOperation) {
if (element.getName() == null) { wrong005 = true; }
operations.add(element);
} else if (element instanceof UmlParameter) {
if (((UmlParameter) element).getDirection()
!= Direction.RETURN && element.getName() == null) {
wrong005 = true;
}
parameters.add(element);
} else if (element instanceof UmlGeneralization) {
generalizations.add(element); } }
即完成輸入資料的分流,用對應的容器儲存起來,再後續按照順序模擬輸入場景完成構建。
類圖
為了迎合後續增容的設定,將類圖處理從umlinteraction中分離了出來,只需進行呼叫即可。該類圖分析器儲存了所有的新構建的類,便於之後新增元素時直接向已經例項化的物件中新增,解決了不一致的問題。
private final Map<String, MyUmlClass> nameToClass;
private final Map<String, MyUmlClass> idToClass;
private final Map<String, Integer> nameToCount; //記錄相同類名對應的類的數目
private final Map<String, MyUmlAssociation> idToAssociation;
private final Map<String, HashSet<String>> classToAssociatedClass; //記錄類的關聯類
private Map<String, HashSet<UmlAssociationEnd>> classToAssociationEnd;
private final Map<String, MyUmlOperation> idToOperation;
private final Map<String, MyUmlInterface> idToInterface;
為了符合類圖中的邏輯關係,“重寫了”部分umlelement類,並通過逐層輸入的方式,完成了模型的構建,接下來分別介紹各個重寫類的架構,值得一提的是,由於assciation關聯的是兩個類,所以需要統籌管理,即在分析類中建立各個類之間的關聯關係而不將其交給各個類分別管理,繼承與實現也是同理。
class:用於儲存所有類圖裡的元素
private final UmlClass umlClass;
private HashMap<String, MyUmlOperation> idToOperation;
private Map<String, HashSet<MyUmlOperation>> nameToOperations;
private HashMap<String, MyUmlAssociation> idToAssociation;
private HashMap<String, UmlAttribute> idToAttribute;
private HashMap<String, UmlAttribute> nameToAttribute;
private HashMap<String, UmlGeneralization> idToGeneralization;
private HashMap<String, UmlInterfaceRealization> idToInterfaceRealization;
association:用於儲存對應的associationEnd
private final UmlAssociation umlAssociation;
private UmlAssociationEnd umlAssociationEnd1;
private UmlAssociationEnd umlAssociationEnd2;
private int end1 = 0;
private int end2 = 0;
除此之外,其中還存有end1與end2是否存在的標誌,供後續查詢操作使用。
operation:用於儲存對應的parameters
private UmlOperation umlOperation;
private HashSet<UmlParameter> umlParameters;
private UmlParameter returnValue;
private HashSet<UmlParameter> inParameters;
private TreeMap<String, Integer> namedTypeList;
private TreeMap<String, Integer> referenceTypeList;
其中存有本體,引數,以及與引數型別有關的容器。
interface:其實大體上與class並無區別,只是少了部分類別的元素輸入
private UmlInterface umlInterface;
private HashMap<String, MyUmlOperation> idToOperation;
private Map<String, HashSet<MyUmlOperation>> nameToOperations;
private HashMap<String, MyUmlAssociation> idToAssociation;
private HashMap<String, UmlGeneralization> idToGeneralization;
private HashMap<String, UmlAttribute> idToAttribute;
private HashSet<String> targets;
private boolean wrong006;
private int operationCount;
private boolean hasDuplicatedGeneralization;
狀態圖
相比起類圖偏複雜的結構。狀態圖倒顯得純粹得多,基本上是圍繞region展開的,課程組為了簡化也刪去了一些內容(好評)
一共重寫了兩個類:region和transition
region:作為狀態圖的畫布存在,存放一個狀態圖的所有內容。
private UmlRegion region;
private UmlPseudostate startState;
private UmlFinalState finalState;
private Map<String, UmlState> idToNormalState;
private Map<String, Integer> nameToCount;
private Map<String, UmlState> nameToNormalState;
private Map<String, MyUmlTransition> idToTransition;
private Map<String, HashSet<String>> stateToStates;//記錄狀態到狀態的直接可達
private Map<String, HashSet<MyUmlTransition>> signToTransitions;//記錄狀態到狀態對應的transition
private HashSet<String> idAlreadyList;//臨時變數,用於記錄已經查詢過的狀態
private HashSet<String> idAnswerList;//臨時變數,用於儲存查詢到的狀態
除了儲存必要的起點,終點,狀態,遷移,還記錄了一個狀態可達的所有狀態,以及狀態間遷移對應的transition。
transiton:儲存與遷移有關的內容
private UmlTransition transition;
private Map<String, UmlEvent> idToTrigger;
private ArrayList<String> triggers;
private Map<String, UmlOpaqueBehavior> idToEffect;
private ArrayList<String> effects;
儲存了遷移的本體以及觸發方法和效果。
順序圖
順序圖的結構無疑是最簡單的,只需要重寫interaction並儲存與其並列的attribute,這裡涉及到attribute的歸屬問題,我的處理辦法是將其同時輸入給順序圖和類圖的處理類,可知一定能在兩者中的一個找到parent,找不到的自動拋棄就行。
interaction:與狀態圖的region類似,也充當畫布的作用
private UmlInteraction interaction;
private Map<String, UmlLifeline> nameToLifeline;
private Map<String, UmlLifeline> idToLifeline;
private Map<String, Integer> nameToCount;
private int lifelineCount;
private Map<String, UmlMessage> idToMessage;
private Map<String, UmlEndpoint> idToEndPoint;
private Map<String, Integer> idToInCount;
private Map<String, Map<MessageSort, Integer>> idToOutCount;
其中存有生命線,生命線之間的傳輸資訊以及endpoint,還記錄了每個生命線發出的訊息個數。
至於演算法,由於要求很低,實在是乏善可陳,就不獻醜了。
二、四個單元架構設計及OO方法理解的演進
1.層次的劃分
拋開作業與作業之間的客觀差距,明顯可以看到第一次作業與最後一次作業在層次劃分上的差異,甚至於第一次作業與第三次作業都存在較大的差別。
第一次作業:
可以發現,架構設計僅僅是為了滿足題目要求的層次劃分,且mainclass承擔了許多不必要的職責,各類之間的層次關係也僅為並列或不明確的層次關係。
第三次作業:
明顯感受到了差別所在,第三次作業將可能細分的所有部分實現了細分,並且各部分之間的關係是可以用一個圖結構來完整表述的,但這樣的設計也是在指導書的明示下才形成的,自身意識還不夠。
最後一次作業:
從一開始便按照各個元素的邏輯關係組織,並且把各個圖的解析放到了不同的類中減少耦合性。
2. java自帶功能輔助架構建立
剛開始建立架構時,多是自己編寫一些方式來展現包含層次關係,後來則是熟練運用java的各種容器來輔助快速建立各種元素的關係。
3. 前瞻性
一開始的幾次作業都是僅僅考慮滿足當次作業的要求而不會思考之後可能的增添的功能是什麼,導致出現多次重寫的情況,不僅浪費了時間,也導致整個架構在頭腦中並不清晰,debug的困難程度超級加倍。第一單元尤為明顯:
到了第二單元得益於課程組實驗課程式碼的代表性,三次作業都在實驗課程式碼的基礎上修改,總歸是少重寫了許多內容。
到了第四單元則掌握了課程組安排內容的規律,在第一次設計架構時就將類圖的處理分離出去,同時建立元素之間的層次關係,之後僅需考慮增添內容即可。
三、四個單元測試理解與實踐的演進
第一單元
前兩次作業是使用樣例資料與自己造的一點資料進行測試,有些碰巧的意味,最後一次作業則引入上兩次作業的強測資料,會相對完備。
第二單元
第二單元的測試是使用自己構造的測試樣例進行測試,但考慮到第二單元內容的特殊性,輔助以直接檢查執行緒內容與執行關係來進行測試。
第三單元與第四單元
這兩個單元的測試私以為最佳的方式還是邏輯的自然推導,在寫完程式碼之後理清要實現的功能是否實現了,只要邏輯順了,無需構造一些特殊的資料點也能實現較完備的檢查。
四、課程收穫
1.面向物件思想的培養
與課程名字呼應,這個學期下來,的確幫助我養成了初步的面向物件的思想,無論是編寫程式碼還是日常生活中,遇到一件事情時,首先會抽絲剝繭,把其中的元素找到,然後分析元素之間的層次關係,最後得出解決辦法。
2.java程式設計能力上升
其實上個學期也選修了一門與java相關的課,但礙於課時等客觀條件的影響,最後效果不盡如人意,甚至最後的大作業都是用的java形式的面向過程方式編寫的,對於java裡的各種元素與關係並不熟悉,但這門課程幫助我掌握了java的基本用法,也理解了java的很多安排。
3.學習方法
本課程獨特的考核機制啟發了我對於學習方法的改進,每次互測時都是欣賞別人程式碼的好機會,每每都會感嘆於dl們的精巧構思,雖然目前刀人數為0,但通過看別人程式碼得到的收穫是一點分數無法比得上的。
五、幾點建議
-
課程指導書的編寫希望能夠繼續改進,指文字上的理解歧義問題,尤其是第三四單元對於指導書的依賴程度很高,出現許多理解上的問題影響都是十分大的(從討論區的火熱程度就能看出)
-
作業之間的聯絡與難度設定,按照基本規律來說,應該是逐漸變難且是一個遞進的關係,但很多時候並非如此,尤其是第二單元,第一次作業屬於地獄難度,但二三單元之間卻十分簡單,尤其是第三單元,甚至於不按照指導書要求的機制實現能夠獲得更好的效能。
-
架構問題。理解課程組希望同學們自行構造的想法,但很多時候很難設計出一個完美的符合後續要求的構造,重寫是相當痛苦的,希望課程組能考慮在指導書中加入關於架構設計的提示,或者給出樣例讓同學們自行抉擇。
結語