前言:
初入java,前三次作業主要是考察物件導向中最基礎也是最重要的類的設計,使之遵循單一職責原則、迪米特原則。如何設計好類、以及如何好處理類與類之間的關係是實現程式碼合理、高效執行的關鍵。作業題目量不大,前兩題是對java語法的考查(如正規表示式,ArrayList,Linkedlist),最後一題比較難,考查了對類的設計,從第一次作業開始,不斷對題目進行迭代,訓練相關思維能力。
1)第一次作業對類的設計和引用不了解,耦合性太強了,而且與單一職責和迪米特法則完全是背道而行。資料在類中的處理時,並且讀入並儲存方式不正確,導致程式碼無論從時間還是空間上都是不合理的。
2)第二次作業比第一次作業稍微好點,邏輯處理好了一點,但後面兩個問題完全沒有解決。
3)第三次作業取消了屬性類的使用,降低了耦合性,同時更靠近了單一職責和迪米特法則,但是時間上可能並不是很友好。
分析:
<1>
設計與分析:
第一次作業不清楚引用,以為物件類不能作為引數傳入方法中,所以設計思路是在主函式中留下一個物件類作為信標啟動各種操作,然後其他的物件類則留在相應類中待呼叫。於是便有了將資料從主函式中讀取存到答案類中再由答案類到試卷類再到題目類的順序進行之後的一系列的操作的結構。
class Answer{
private ArrayList<String> answer = new ArrayList<>();
private Exampaper exampaper;
private ArrayList<String> tf = new ArrayList<>();
public Exampaper getExampaper() {
return exampaper;
}
public void setExampaper(Exampaper exampaper) {
this.exampaper = exampaper;
}
public ArrayList<String> getTf() {
return tf;
}
public void setTf(String tf) {
this.tf.add(tf);
}
public String getAnswer(int i) {
return answer.get(i);
}
public void setAnswer(ArrayList<String> answer) {
this.answer = answer;
}
public Answer(int n, String[] str){
String[] ans = str[n].split("\\s*#A:\\s*");
for(int i = 1; i <= n; i++)
answer.add(ans[i]);
exampaper = new Exampaper(n,str);
}
public void booltfA(){
for(int i = 0; i < exampaper.getNumber(); i++)
tf.add(exampaper.booltfE(i, answer.get(i)));
}
public void printfA(){
for(int m = 0; m < exampaper.getNumber(); m++){
exampaper.printfE(m);
System.out.printf("%s\n",answer.get(m));
}
System.out.printf("%s",tf.get(0));
for(int n = 1; n < exampaper.getNumber(); n++)
System.out.printf(" %s",tf.get(n));
}
}
但是這樣設計如上面程式碼可見,基本上這個完整的答題判題程式程式碼都是靠這個答案類去完成的,其他的類只是起到輔助幫助作用,不符合類的設計原則。而且假如我想儲存題目類的資訊,要從主類到答案類再到試卷類最後到題目類這樣子的鏈式結構。耗時增加。而以上引導我這樣寫的原因是我對java中引用的不瞭解。並且由於對引用不了解,所以對於每份答案,都會存一套試卷和答案,增加了程式碼執行所需時間和空間。
改進建議:
在主函式就把所有資料傳入三個類中,用引用進行諸如將試卷類的題目物件指向main裡的題目物件。再新定義一個正規表示式類進行資訊的儲存和判斷類進行答卷的批改。
<2>
設計與分析:
由於第一次程式碼結構不對,於是第二次作業相較於第一次,取消了原來以答案類為主體的鏈式結構程式碼,而因為試卷類與題目類、試卷類與答案類都是直接聯絡的關係,並且這也符合迪米特法則,所以採用了以試卷類為主體的樹狀結構,先把試卷資訊全部存好,再將與試卷相匹配的資料存進相關的類物件中,雖然有點耗時,但相比原來題目和試卷都多存了來說,第二次程式碼只多存了題目資訊,節省了資料儲存的空間。並且由於所有資料在儲存完之後都是已經排好序的存在,所以答案的批改十分的容易。
當然這也有一個漏洞,假如有答案號與試卷號不匹配的話,程式碼是處理不了這個資料的。於是,當時寫完了之後為了應付這個漏洞,又加了一個新的類用來存答案卷的編號以作後面的輸出操作。所以雖然這樣設計非常簡單無腦,但是它依舊多耗了記憶體,資料多了之後時間也耗的多。
採坑心得:
撇去最不應該的漏掉的答卷號與試卷號不匹配的犯錯,就說當時折磨我很久的最後一個測試點,出於習慣,我給答卷類按答卷號排了序,導致答卷資訊按答卷號順序輸出:
如圖,s:12在s:1的前面,因為對答卷進行了排序,導致了輸出結果s:12在s:1的後面。而正確的結果s:12應在s:1的前面。其實這應該是我沒有想全面,題目沒有特別強調,而假如是按順序輸出的話,2個s:1又是按先後輸出,不太合理,所以應該是按先後輸出。
改進建議:
這樣子的結構耦合性依舊很高,而且試卷類在本身的功能外,還充當了一個代理類集合的角色,完全違背了單一職責原則,應該多加幾個代理類分開試卷類多餘的方法。在主函式就把所有資料傳入三個類中,取消與試卷相匹配資料存入模式,直接存入物件類組,再新定義一個代理類正規表示式類進行資訊的儲存和代理類判斷類進行答卷的批改。
<3>
設計與分析:
為了降低耦合性,我完全刪減掉了屬性類。相比上面三個類還加了正規表示式類、判斷類以及題目新加的學生類、刪除類。透過正規表示式類分割完不同的資訊後再返回不同的值經過switch case語句來新增到相應的類中。再透過刪除類中的方法刪除指定題目,然後透過判斷類中的方法判斷試卷是否滿100分,之後透過學生類中的方法對連結串列中資料進行排序補充,使之與試卷上的題目順序相匹配,最後再進行輸出。但是如果這樣的話,過多的for迴圈可能導致時間效率上不高。
採坑心得:
之前有挺多小錯誤的,這裡只挑困住我好久的2個問題。
1.正規表示式出錯:
如圖,因為題號後面的分數漏打“+”了,導致輸出結果輸出了wrong fomat,這個問題讓我看了好久都沒發現哪裡出錯,所以每寫完一段正規表示式都應該停下來檢查一下,趁著記憶和思路都還在,看下是否寫錯。不然之後可能就好找了(笑)。
2.提取的資料兩端有空格:
如圖,1+1=2,顯然是正確的,但是判斷結果顯示它錯了,因為沒有把標準答案前面的空格去掉,導致判斷時是空格加2與2做判斷,自然會顯示false。雖然這也可以用正規表示式處理的,但我想把它單獨拎出來,它應該是一個需要養成的習慣,在每個處理的資料之後都要加個trim(),確保不會出現問題。
改進建議:
雖然耦合性降低了,但是for迴圈寫了很多。導致輸出程式碼部分顯得很臃腫。但其實可以稍微往類中加幾個屬性類,例如刪除類中加題目類組,答卷類中加學生類之類的,這樣可能會更好一些。我的正規表示式也寫的不好,有些漏洞,需要改進。刪除方法是想把類中的除題目號之外的屬性變成null,結果忘寫了,這個也得加上。
總結:
這三次作業不僅讓我掌握了更多java語法,如正規表示式、ArrayList、Linkedlist、hashmap、眾多時間類庫函式,更重要的,我們的思維在做最後一題的類設計的時候在不斷改進、不斷地積累提升我們的類設計能力。同時,我也知道我的java語法水平還有待提高,我的正規表示式、我的hashmap都還沒有熟練掌握,時間類庫函式也還沒有完全瞭解。語法之於物件導向技術無異於地基之於大廈,一定要打好語法基礎,只有熟練掌握與應用語法,才能讓之後的java之旅事半功倍。希望老師多給點機會,講點例子讓我們積累提升向七大設計原則靠齊的類設計思維能力,同時,我們應該不斷思考,任何事物都可以供我們抽象,供我們使我們的思維向七大設計原則靠齊。