OO第五、六次大作業總結

时倾乄發表於2024-06-10

一、前言

  在經過了第一階段的迭代大作業之後,我們迎來了新的迭代大作業-智慧電器模擬系統。

在這個階段的大作業中,我們需要編寫模擬家用電器與電路的程式。

二、設計與分析

  在經過了第一次迭代大作業的折磨之後,我總結之前出現的問題,在之前的大作業中,我在類的設計方面做的並不好,每當更新一次大作業之後,我總是要做不少的改動,甚至幾次直接重寫整個專案,這給我帶來了不小的麻煩,同時花費了我很多的學習時間,然而結果卻總是不如意,幾次和滿分就相差那麼幾分,但是卻始終得不到。並且在之前的大作業中,我發現每當我過幾天再看程式的時候,往往會不記得寫的是什麼意思,這導致我總需要花一定的時間去閱讀之前寫的程式碼,這一點同樣花費了我不少的時間,這些即吃裡又不討好,而吸取了上次大作業迭代的經驗之後,我在寫這次迭代大作業之前先是花了更多的時間去思考如何構建類,並且明晰了類與類之間的關係之後我才真正開始寫程式碼,並且在寫程式碼的過程中,我還時常在程式碼後面新增註釋,以方便後續的閱讀和修改,而這次類的設計較為萬山也帶來了巨大的好處,在寫第二次的迭代的時候,相比起第一次迭代,我的程式碼相比於第一次迭代時提交的程式碼只多出了30行左右,從第一次迭代的650行到第二次迭代的680行,這也讓我體會到了類設計在大專案中的重要性。

這是我在開始寫之前大致寫的類圖

OO第五、六次大作業總結

在這個時候我一共設計了12個類,雖然存在一定的問題,但是已經偏向於完整的了,首先我是想著先做一個最大的父類 裝置類(把所有電路中的電器元件都視作裝置),這樣有利於使用多型,不過過多使用繼承容易導致一旦父類修改了,子類就很容易需要大改的情況。所以當時我在寫這個類的時候花了很多時間進行思考,當然結果是好的,在之後的編寫中,這個類基本沒有什麼修改的。其下面分為了控制器類,例如電壓調速器和開關等等。而另一個即是電器抽象類,作為所有電器的父類,而在這個父類之下我又將不同種類的電器的類別分類做了各自的抽象父類,便於後續新增一些同種類的電器,就比如第一次迭代就有的日光燈和白熾燈的區別,不過當然這裡其實沒必要分得這麼細,我這麼寫其實是考慮到加入之後加入一些比較特殊的電器的情況,所需要設定的屬性就不太一樣了。除此之外,我在裝置抽象類下還寫了另一個子類,引腳類,作為每個電路元件的引腳。而右上角的兩個類分別為控制類和連結類,前者控制電路的操作,比如指揮連結類將電路中的各個元件進行連線,以及根據輸入資訊對元件進行修改,比如開關的開關或者連續調速器設定引數。

下面這張圖是我在寫完了第二次迭代之後的類圖

OO第五、六次大作業總結

我在其中加入了電路抽象類,其下為串聯電路類,並聯電路類。並且我把原來設計類圖中的裝置類之上新增了電子元件類,這考慮到新增的電路類並不算是一個裝置,而且將引腳劃分為電子元件類會合理一些,同時裝置類在程式最後都要輸出各自的資訊,就可以直接使用多型輸出資訊了。

比如下面這個輸出方法。在裝置類中設定一個抽象方法display(),而其子類的所有的控制器以及裝置都會輸出相關的資訊。

public static void show(LinkedList<CircuitMember> members){
    for(CircuitMember member : members) if (member instanceof Equipment) ((Equipment) member).display();
}

在這個類圖中也可以看到我新增了一個電源類,設計這個類的時候我是考慮到假如輸入電壓不是220v的時候那要根據不同的電源輸出不同的電壓,雖然這幾次迭代大作業中看老師的題目描述感覺不太可能出現這種情況,但是設計了這個之後我覺得這一套類的設計更加合理了一些,並且在設計這個電源類其實有利於我所編寫的程式執行,因為我所編寫的程式其實最重要的部分就在於不同電子元件的連線上,我的思路是把所有的電路元件連線好之後只要從初始埠輸入電壓之後,整個程式就會真的跟一條電路一樣自行根據設計好的執行方法執行。因此我需要一個電源類作為整個電路的初始輸出埠。

powersourse.Run();

所有的電路元件只需要這一行輸入電壓之後就會自行執行好,所以這個電源類的設計其實會使程式更加合理。

OO第五、六次大作業總結

上面這張圖是使用SourceMonitor後的檢測結果,在這張圖中,可以看到LinkCenter類的行數最多,達到了113行,語句數量也達到了97行,因為其中連線電路的部分佔了大頭,但是其中的註釋佔比還是不夠高,只有10.6%,這也是其中的一個缺陷,其中方法的最大的複雜度達到了9,這個最大的複雜度就出現在了下圖的findAllMembers方法中,因為這個方法是透過輸入電路資訊建立所有電路元件的,其中使用了switch語句根據元件名稱的首字母建立物件,因為有9種元件所以複雜度為零,這個地方在之後也應該進行修改降低其圈複雜度。

OO第五、六次大作業總結

而其實從整個統計圖上來看,最為複雜的地方就是LinkCenter類,其中連線電路部分使用了不少的迴圈語句和if語句,在日後寫的過程中有必要對其進行修改,做出進一步的最佳化。

其次再看其註釋佔比例,其實雖然平均下來只有8.6%,但是相比第一階段的大作業迭代要好了很多了,至少在大部分比較複雜的方法後加上了註釋,這至少幫助我在寫第二次迭代的時候要方便不少了,至少不至於像之前那樣閱讀大量原始碼才能想起來之前的寫法了。

三、踩坑心得

  1.在第一次迭代之前設計類的時候沒有考慮到串聯電路和並聯電路這種可能,所以在第一次提交的時候遇上了電路種開關在電器之後的情況的時候出了錯誤,因為我的設計思路是電路從輸入端自行執行到最後,當斷路開關在電器前面的時候電路就不會往後輸入電壓了,但是當斷路開關在電器後面的時候就無法偵測到這種情況了,就好比一個人走在一條單行道上,當前面有一堵牆(斷路開關)的時候,他就往前走不了了,牆之後的蛋糕(電器)他就吃不到了。而如果這堵牆(斷路開關)在蛋糕(電器)後面呢,他能先吃到蛋糕之後再遇到牆,對比到電路種這就導致了再電路斷開的情況下電器卻成功執行了。所幸在我自己設定測試點的情況下,發現了這個問題,於是新增了一個串聯電路類處理電路中的電子元件的斷路情況,就好比有一個人A是一條路的管理者知道路中的各種情況,一個人B走在這條路上,A會在B走進這條路之前替他看看這條路能不能走,能走B就可以經過這條路上的所有電器,不能走A就阻止B進入這條電路,這樣B就不會接觸到這些電器,自然就不會出現斷路情況下電器執行的情況了。

  2.在這套題目的要求情況下,最後程式要按照順序輸出電器的資訊,但是這套題目中的電器種類非常多,一共7種,我想透過實現Comparable介面中的compareTo方法實現排序方法,但是如果按照迴圈的方法寫,其複雜程度一定會很高。而且程式碼行數肯定也不會少,所以我在思考後想到了一種方法

public int compareTo(CircuitMember e) {
    final String EquipmentName = "KFLBRDA";
    int index1 = EquipmentName.indexOf(getName().charAt(0));
    int index2 = EquipmentName.indexOf(e.getName().charAt(0));
    int result = index1 - index2;
    if(result == 0) result = getName().compareTo(e.getName());
    return result > 0 ? 1 : -1;
}

透過設計一個字串,其中包含所有電器的首字母然後對比兩個物件的首字母在字串中的下標,然後透過比較下標的前後,然後返回1,-1 ,0,當首字母相等的時候,就使用原字串的compareTo方法返回值,這是為了處理對於當兩個物件為同一種裝置的情況(裝置名字首字母相同,但編號不同.在這種方法下,裝置的排序方法就會簡單很多,並且只使用到了一個if語句(當下標相同時),同時相比較大量的迴圈語句而言,這樣的程式碼執行方法會更快,在後續的迭代種如果新增了一些新的裝置的時候,就只需要在字串最後新增新裝置的首寫字母就可以了,而不是像迴圈語句中的那種還要新增新的迴圈語句,從可擴充性而言,遠比採用迴圈方法更好。這一點雖然不算是踩坑點,但是作為一個心得更好。

四、改進建議

  我認為可以新增電器短路的情況設計,設定電器短路,當電器短路,電器本身不工作,但電流會透過電器。

五、總結

設計類的時候要考慮更加全面周到:設計類的過程十分重要,這個階段為後續程式的執行打下基礎的部分,所以在設計類的時候,要考慮周到,從自己的思路角度思考程式碼執行的時候會遇到什麼問題,並且該如果在設計類的過程將這個問題給避免掉,就比如踩坑心得中第一點說的忽略了串聯電路中開關在裝置後面並且斷路的情況。

要繼續養成新增註釋的習慣:持續養成新增註釋的習慣,這不僅是在面對大專案的情況下使程式碼更加通俗易懂的方法,也是對程式碼進行修改時候的利器

面對一些大迴圈程式碼時要思考能不能用另外一種更高效的方法替代:就如心得中第二點中的一樣,當面對一些需要多重迴圈的程式碼時,要勇於思考一些新的方法去避免這種大迴圈。

相關文章