0.寫在最前面的廢話
度量分析的工具真的好難搞!到現在我的metrics也無法正常使用,可以建立它的透視圖,但是不能再工程的屬性裡找到它......得,我現在隻能展示類關系圖譜(逃)。
OO給我的感覺就像是雨夜登山一般,“登山”本已舉步維艱,大晚上的還漆黑一片,得“邊爬邊摸索”,要是走了面向過程的“歪路”,也難免重頭再來。加之各種條條框框如大雨般傾盆而下,一不小心就弄個“泥濘不堪”,如果不慎落了“無效作業”的大坑,也隻能叫苦不疊但無力回天。
嗨,這麼一寫還突然覺得,OO有點意思。
1.第一次作業
第一次作業是要寫多項式的加減,我的程式的大體結構如下:
其中,Main是主類,負責調用其他的類,并運作程式;
Check類是用來檢測輸入的字元串是否合法的,在這裡主要使用了正規表達式來判斷;
Operation類是用來實作多項式加減的,主要是一個大循環,一個字元一個字元向後讀。
第一次作業的公測錯了兩個點,互測沒出問題,第一個是爆棧,原因是我的正規表達式就是一個長串來比對整個的輸入,導緻在最大長度的輸入情況下程式出了問題。把輸入的幾十個多項式按花括号拆開,再進行正則比對即可解決。
第二個點是沒有考慮-0還是什麼關于正負号的一個小點,後來仔細閱讀指導書後,大呼之遂心服口服,這很好的提醒了我在以後的作業中要仔細考慮各種情況以及仔細閱讀指導書,勤于思考各種非法的情況。
我的第一次作業的代碼量比較少,可能勉強算是一個有點,但第一次寫面向對象的程式,總是有些四不像的感覺,不會使用構造函數,把所有的功能都放在了類的方法中,形似面向過程。
2.第二次作業
第二次作業是寫一個服從“傻瓜排程”的電梯,即根據指令的時間順序來控制電梯的運動,我的程式的大體結構如下:
其中,Request類是用來處理單條需求的,通過正規表達式判斷指令是否合法,進而判斷指令是哪種類型(FR+UP,FR+DOWN,ER),然後将指令分解,分别去判斷時間和樓層是否越界。因為考慮到時間可能超過int型的範圍并且會有很多的前導零,我用了一個循環來算時間...
for(int i=0; i<s.length; i++ ) {
if(s[i]!= '+') {
NowTime *= 10;
NowTime += (s[i] - '0');
}
}
Queue類是用來循環讀入指令的,調用了Request類,對單條指令進行處理,并把它們分别存在對應的數組中。
Elevator類是用來記錄電梯的運作狀态的,包括電梯現在所處的樓層,下一個樓層,目前時間,下一個時間,電梯運作時間,運作狀态等。
Floor類是用來記錄樓層資訊的,但其實實際操作中我幾乎沒用到它,因為Elevator類裡的東西就夠用了。
Scheduler類是用來執行電梯排程的,執行個體化了Queue類的對象來讀入指令,也執行個體化了Elevator類的對象來記錄電梯的運作狀态,并設定了一些其他的屬性來模拟電梯的運作。
private double[] ERButton = new double[20];
private double[] FRUPButton = new double[20];
private double[] FRDOWNButton = new double[20];
上面三個按鈕數組用來模拟電梯裡面和樓層上的按鈕。開始時我想用boolean型的數組,但是發現那樣隻能記錄按鈕的狀态,不能記錄按鈕狀态改變的時間,是以我後來改用double型,記錄每個按鈕被釋放的時間,即這個按鈕在哪一個時刻可以被再次使用,在這個時刻之前,如果有人發出了需要這個按鈕的指令,則被視為同質指令。
if(queue.RequestTypeArray[valid] == 1) {
if(queue.TimeArray[valid] <= FRUPButton[queue.FloorArray[valid]]) {
System.out.println("#This input is similar:"+(queue.InputArray[i]));
IfSimilar = true;
}
}
拿一段代碼來說明,比如一條指令的類型為1(FR+UP),那麼比較讀入指令的發出時刻與該指令的對應按鈕的釋放時間,就可以判斷這條指令是不是同質(在這之前先判斷是否合法)。這樣就不需要預先知道後面的指令是什麼,簡化了代碼的複雜度。
如果它不同質,那麼就可以産生一個對應的輸出,并改變某個按鈕的釋放時間,部分代碼如下:
if(queue.RequestTypeArray[valid] == 1) {
FRUPButton[queue.FloorArray[valid]] = elevator.NextTime;
}
比如這條指令的類型是1,那麼就将FRButton中對應按鈕的時間設定為elevator.NextTime(即經過計算後的電梯達到下一個樓層的時間),這樣就可以讓程式不斷的循環運作了~
第二次作業的公測我沒有錯測試點,互測錯了一個,是:輸出如果超過了int範圍并且是小數,那麼我的輸出就會把它的小數部分給省略掉,後來我尋找原因,發現在這個地方:
System.out.print(BigDecimal.valueOf(elevator.NextTime));
我為了使輸出不是科學記數法的形式,我用了BigDecimal,但不知為啥這裡會出這樣的幺蛾子......
第二次作業我花了很久的時間去思考,主要在想如何去判斷各種同質的狀況,後來經過不斷的摸索,并且有意識地去模拟真實電梯的屬性和方法(在我了解裡的面向對象吧),我選擇用按鈕這個概念來記錄電梯的被觸發不同狀态後的終止時刻,想明白了這個之後再去寫代碼就順暢了許多。有了這次的經驗,我在寫第三次作業之前花了更多的時間去思考。
3.第三次作業
第三次作業的電梯是在第二次作業的基礎上考慮“捎帶”這一規則,我本以為隻需要小改一下就成,後來在實際操作中我才發現自己錯的離譜......先展示一下程式的大體結構:
其中,除了SubScheduler類以外,其他幾個類與第二次作業基本相同,不贅述;
SubScheduler是繼承了Scheduler父類的子類,說來慚愧,我這次作業幾乎隻幹了一件事兒:重寫了Scheduler父類的Run函數......
因為電梯要捎帶,那麼電梯的運作就不能隻是發生在相鄰的兩條指令之間了,因為後面的指令或者時刻靠後的指令完全有可能要先執行,判斷哪些指令要先執行就是最大的難點所在。我先很自然地想到我們使用的電梯是什麼樣的,在運動到某一樓層前,如果接收到這個樓層發出的信号,如果方向相同便可以停靠,那麼關鍵點有兩個,一個是位置,一個是時間。我最初想到的是以時間為主線,直接以0.5s為最小的度量,每過0.5秒記錄電梯的位置和運作狀态,然後去隊列中搜尋有沒有這個時刻或之前發出的同層的指令,後來因為各種考慮放棄之。
于是便選擇了先周遊一下讀入指令後面的指令,如果這條指令滿足可捎帶的條件,執行之,然後把它從請求隊列中剔除,并改變一些按鈕被釋放的時間以及樓層、到達時刻等屬性,其中一部分大概像下面這樣:
else if(elevator.Condition.equals("UP")) {
if(queue.RequestTypeArray[j] == 1) {
if((queue.FloorArray[j] <= MaxFloor) && (queue.FloorArray[j] > elevator.NowFloor)) {
ExtraTime = (queue.FloorArray[j] - elevator.NowFloor)*0.5;
for(int k=(elevator.NowFloor); k<(queue.FloorArray[j]); k++) {
ExtraTime += ExtraFloorArray[k];
}
if(queue.TimeArray[j] < (elevator.NowTime + ExtraTime)) {
if(queue.TimeArray[j] <= FRUPButton[queue.FloorArray[j]] ) {
//同質
UsedArray[j] = true;
System.out.println("#SAME["+(queue.ValidArray[j])+"]");
}
else {
FRUPButton[queue.FloorArray[j]] = (elevator.NowTime + ExtraTime);
ExtraFloorArray[queue.FloorArray[j]] = 1.0;
//加入捎帶的額外數組
UsedArray[j] = true;
ExtraConditionArray[queue.FloorArray[j]] = "UP";
ExtraOrderArray[queue.FloorArray[j]][ExtraSameNumberArray[queue.FloorArray[j]]] = j;
ExtraSameNumberArray[queue.FloorArray[j]]++;
}
}
}
}
很繁瑣,分了電梯運作的各種狀态以及指令的各種類型來分類讨論,這裡把主指令之後的指令存入一個新的數組中,而不是直接輸出,因為它後面還可能有比它還要提前執行的指令,是以輸出也變得很繁瑣了......
if(elevator.Condition.equals("UP")) {
for(int m=elevator.NowFloor; m<=MaxFloor; m++) {
if(ExtraFloorArray[m] == 1.0) {
for(int n=0; n<ExtraSameNumberArray[m]; n++) {
if(queue.RequestTypeArray[ExtraOrderArray[m][n]] == 1) {
System.out.print("[FR,");
System.out.print(queue.FloorArray[ExtraOrderArray[m][n]]);
System.out.print(",UP,");
System.out.print(queue.TimeArray[ExtraOrderArray[m][n]]);
System.out.print("]/(");
System.out.print(m);
System.out.print(",");
System.out.print(ExtraConditionArray[m]);
System.out.print(",");
System.out.print(BigDecimal.valueOf(RunTime));
System.out.println(")");
}
我在主請求結束後進行這個輸出的循環,大體思路是上面的程式段記錄了每個樓層的停靠狀态,之後周遊樓層,把有标記的樓層按要求輸出。這是一種以位置為主軸的方法吧。因為一層樓中可能會開若幹次門,是以隻能建立一個二維數組ExtraOrderArray來記錄某層樓的可能出現的若幹次開門的請求。
很幸運,第三次作業的公測和互測都沒有出BUG,可以說是很振奮人心了。但想到之後要寫多線程的電梯,感覺程式難免還是要修改,還是很令人頭大。
這次作業雖然重點隻是放在了重寫Run函數上,但周六日兩天恰好有事情不能寫OO,結果導緻我的周一二三過的相當艱難,通宵數宿,苦不堪言,不堪回首,觸目驚心......想不想得清楚是一方面,更重要的是一些小的細節的改動會牽一發而動全身,本來想要微調結果不得不大刀闊斧的改一通,其中折磨可想而知。這也讓我吸取到了很寶貴的經驗:一、拖延症害死人,能早點解決的事兒别把它養肥了;二、磨刀不誤砍柴工,在頭腦清醒時先把問題想清楚,方法想完善,必要時可以手寫一下流程架構圖什麼的,這樣說不定就能事半功倍。
總的來說,OO還是讓人收獲頗豐的,别的不說,就是以後得去“搬磚”,基本的程式設計能力還是要有的,毋庸置疑,OO鍛煉了我的程式設計能力;其次,在思考和對問題的分析上,我可以有意識地用面向對象的思路去思考問題(或許我還并不能正确認識什麼是面向對象),這是一種有趣的思考過程,我總感覺這樣幻想出來的對象還有點可愛......
最後祝大家永不被判無效作業!實在太痛了,也希望以後的OO能更新一下無效作業的判斷機制吧。