第一次迭代總體來說不難分析。它每個控制器和受控制器都是獨立的,設計完之後就可以像拼積木那樣來完成專案。感覺是封裝思想的經典例題。
在進行類的設計時,由於題目中描述的類的實現採用的是元器件拼音首字母,因此為了方便記憶,我也使用了對應的拼音來定義類。這在實際使用時是不合適的。拼音並不是作為官方語言來使用的,容易對閱讀性和程式碼習慣造成一些麻煩。
在設計受控裝置類時,繼承的setPower方法會根據輸入電壓分情況來輸出。題目中雖然沒有給出實際的表示式,但是可以用兩點算出直線的算式。不過要注意的是,算式中的數值應當儘量準確,比如說300/7可以直接寫成42.85714285171,否則由於浮點數運算精度的問題,得出的值將小於預期。例如下圖。
同時邊界值要直接使用題目中給出的值,不然去尾輸出為int值後會導致誤差而無法透過測試點。(過程值都是double型別,只有最後輸出時才強制轉換為int型別)
在設計控制裝置類時,所有的檔位預設為0,開關預設為關(false)。原本打算開關也用檔位屬性,但當時覺得這不符合常識,覺得開關就應該用Boolean型別,所以用了Boolean型別的OnOrOff。但實際上也是可以做到的——(預設檔位為0)呼叫檔位設定方法檔位--就可以了;相反的情況,檔位為1時檔位++就可以了。
這裡或許可以將“OnOrOff”改成“isOn”,這樣意思沒有改變,也更容易閱讀。
在上述兩種設計類時,雖然說有使用繼承,但是也只是用了,基本都是重寫而不是super,結構上看起來好一點而已。不過總比沒有考慮好得多。只能說實際操作是一方面,思想又是一方面。
在設計主類時,由於只有一條電路,因此除輸入電壓VCC和接地GND外,所有地方的電壓都是相同的。因而我在主類定義了一個靜態變數VCC,至於為什麼不是final而是變數,是為了讓它被調節器作用後可以直接使用。這樣在用電器設定輸出情況(亮度、轉速)時可以將VCC直接傳入,列如上圖中白熾燈類的setPower方法。
對於元器件,每個都逐一進行了初始化,這樣後面就可以根據contains等方法讀取到的字串直接進行類的實現。但是這裡沒有考慮電路存在多個相同元器件的情況。
在讀取輸入時,我沒有按回車而是按空格讀取字串,然後取巧地每隔一個讀取一次(當然這也只適用於電路連線正常的情況)。使用了String類的substring方法與正規表示式擷取前兩個字元(如F1)來進行名字的設定,同時將該名字存入String連結串列以便後期遍歷。
透過遍歷連結串列獲取的字串來判斷已有的元器件種類,據此來輸出元器件的編號和工作情況(檔位、亮度、轉速等)。
這裡有一段有趣且實用的程式碼。在設定調節器時,透過呼叫方法,用正規表示式來得到double型別的數來設定檔位,如果沒有匹配到,將丟擲異常(本次迭代不會遇到),這種考慮符合健壯性的思想。具體方法如下。
在最開始,我新建了中間量doubleValue來儲存並返回,但idea警告說該變數是冗餘的,修改後如圖。
從中可以得到這樣一點信條:如果可以直接返回的話,不需要新建一個過程量。
在最佳化方面,或許可以採用類的連結串列來儲存元器件,這樣就省去了根據名字匹配元器件的過程。
圈複雜度方面,這裡使用了idea的MetricsReloaded外掛進行分析,如圖。
從上圖中可以不難分析,程式碼、主要是main方法的圈複雜度較高——具體來說,使用了大量的if-else巢狀來匹配不同的裝置,從而導致了整個程式圈複雜度綜合較高。其他方法的圈複雜度均不大於3。就這點來說,main方法有程序導向之嫌。
最佳化方面,或許可以將if - else if結構、do while迴圈提取成一個單獨的方法,從而減少內部的分支結構,降低main方法的圈複雜度。
第二次迭代的程式碼與第一次相比,圈複雜度更是爆炸性的,main方法的平均複雜度來到了驚人的118.5,如圖。
原因也顯而易見——大量的if-else巢狀,for迴圈巢狀,甚至在switch裡巢狀if-else,這裡舉一段程式碼給大家看一下,盡是這樣的程式碼,非常可怕。
使用窮舉法來達成的演算法就和氣泡排序一樣,做了很多不需要的事情。雖然可能是我沒有設計圖中所畫類圖裡的電路類造成的,但是我還是沒辦法想象,如何設計一個電路類使得它執行良好。這也是書到用時方恨少的一個體現,要不以後就叫這個名字吧(笑)。總之,閱歷是需要積累的東西。
窮舉法帶來的另一個缺點則是,你沒法確認你是否真的窮舉了。你舉的例子也只是你能想到的例子而已,可能最後也只是冰山一角。我也只是考慮了題設中給出的測試點,即每個元件最多有三個——電路1和電路2並聯後與電路3串聯,然而並聯方式是否有其他情況,每條電路存不存在2個電路裝置以外的情況——我沒有考慮,也沒有時間考慮了。
光是寫完那些超長的if-else巢狀、for迴圈巢狀、switch巢狀if-else,就花了大量的時間。保證不看花眼就已經很累人了。這也是窮舉法的一個缺點。
一複雜就沒辦法思考,這個壞習慣還是沒法輕易改掉。要是突破了這個桎梏,說不準就能摸到更高的門檻。
說完缺點後來談談第二次迭代的新增注意點。
輸入電壓VCC設定為final型別,這樣符合實際使用情況。在電路中,雖然可能會因為調節器導致實際電壓改變,因為多個用電器導致分得的電壓不同,但是VCC到底還是VCC,並沒有因此而改變數值。檔位調節、分配電壓時再進行運算即可。這種考慮符合安全性的思想。
由於題中未給出元器件的電阻,因此這裡只能假設控制裝置不分壓,而受控裝置在電路中只有一種(防止其他受控裝置電阻不同),這樣電壓的分配就明確了,然後用物理知識算出即可。
而對於類,本次採用了陣列來儲存,每一個陣列也都建立了對應的變數count來記錄使用的次數,這樣最後for迴圈輸出時就不會發生越界問題。另外,由於錄入過程中setName時會發生name未定義的報錯,因此在初始化時就將所有元器件命名為“NULL”。
在遍歷陣列輸出元器件的編號和工作情況時,由於錄入電路裝置時順序可能是亂的,需要根據名字來排序並輸出。由於時間很緊,這裡採用了直接的氣泡排序(畢竟每個元器件不超過3個,演算法的優劣性不明顯)排序後輸出。本來有打算使用介面來獲取名字,然後用該介面的方法來解決排序問題,但由於不熟練因此一直報錯,最後不得不放棄。程式碼如下,也只是給出一種未實現的解決方案。
這兩次迭代可以得出的結論是,如果發現有大段功能結構相似的程式碼,最好是提取成一個方法;如果有簡單的方法,最好就採取更簡單的方法。