OOP第三次部落格

破疑發表於2024-06-28

write_by_23201707_gongjunjie

oop第三次部落格

一:前言

這次部落格不出意外是oop課程的最後一次部落格了,不過這次部落格pta只有兩題,但是我想說的是,最後一次pta也是夠難的,
但是好像我自己的設計也有很大的問題,第七次pta遺留下了一點問題,導致第八次出現了很多問題

二:關於三次pta作業

題目依然是電路的迭代
知識點:
1.需要進行類的設計,對陣列進行封裝,要考慮類間關係
2.此次pta使用了正規表示式匹配
3.需要保證類的單一職責原則
4.達到開閉原則,以至於後面迭代的時候可以透過加類而不是瘋狂改程式碼
5.需要用到list
6.需要一點電學基本知識
題量:
第七次的題目量其實還是可以的,我的設計還是可以應對第七次的pta,不過一開始也是改了一段時間,總感覺自己的設計開閉性不夠好,
第八次就是比較難的了,我也是用了整整一天才把基本邏輯給寫出來,然後又花了好多時間去琢磨測試點,也是比較令人頭大
難度:
本人認為第七次的難度還是中等的,因為這次的迭代並沒有顛覆我的設計,所以我可以繼續按我以前的邏輯寫,不過還是有幾個測試點過不了,經過一段時間就能改出來,
第八次pta就比較的難寫了,尤其是後面還加了幾個測試點,就有點過不了測試點,不過我的程式也確實有點問題

三:關於設計與分析

第七次pta作業

關於類設計

本次設計和大體還是依據上次的設計加上了互斥開關和窗簾兩個類,然後加上了一些方法去處理這兩個類,互斥開關是繼承的Control控制類,窗簾繼承的BeControlled被控制類
但是窗簾還要和燈進行關聯,所以我就將日光燈和白熾燈進行抽象成一個燈類,然後再繼承,這樣就可以利用多型處理窗簾的開關問題

分析程式碼

1.首先就是關於互斥開關的處理,一開始寫完有好幾個測試點沒有過,互斥開關的1-2引腳預設是閉合的,1-3引腳是不閉合的,所以我就生成了兩個物件,然後如果引腳是1-2,直接是閉合狀態,而1-3引腳就是預設狀態是不閉合的,這個處理我寫在了一開始建立這個物件時,利用正規表示式提取設定互斥開關物件的引腳物件,然後寫了一個設定其開閉狀態的函式,在生成物件後就對其狀態進行設定,如果是1-2引腳就閉合
2.關於窗簾,問題在於和燈的關聯設定其開閉的尺寸,所以我在當所有的資料都讀取完後並進行初始化物件後,將我的連線的最大類傳給窗簾計算開閉大小的方法中,然後遍歷這個陣列找到燈型別,然後將他們的亮度相加,在進行對窗簾的開閉尺寸進行計算

3.關於改變互斥開關的狀態,我是將其1-2引腳和1-3引腳的物件的狀態都進行改變

踩坑心得

1.第一個需要注意的地方是互斥開關只輸出1-2引腳的開閉狀態,1-3的是不輸出的,所以輸出這裡要加以限制
2.電路中可能會出現只有1-2引腳,或者只有1-3引腳的互斥開關的情況,如果是1-2 引腳其實還是正常輸出,但是1-3引腳就有點麻煩了,因為沒有1-2引腳的互斥開關的物件,所有我寫了一個方法,去搜尋是否存在這樣一個物件,如果只有1-3引腳沒有1-2引腳,那麼我會判斷此1-3引腳互斥開關的狀態,然後在最大的link陣列直接加上一個1-2引腳的物件,並且其開關的狀態是和1-3引腳相反的,這樣輸出的時候就可以直接輸出1-2引腳的互斥開關的狀態
3.電路會出現短路問題,這個問題就比較的難搞,我是根據並聯電路中某個串聯電路其總電阻是否為0,而且這一路要是通路,這樣和它並聯的其他路就是被短路了,
而被短路的那一路,我就將其電壓設定為0,而且將其電阻返回值為0,不然算總電阻時就會使電阻多加上了被短路的電阻
4.此次電路會加上多個並聯電路的串聯,但是因為我上次的迭代提前寫了這一點,所以也是很簡單的過了這個點

改進建議

1.對於結構上可以進一步最佳化,對每一種型別的電器進行抽象,後面可以更加簡潔的使用這些電器
2.對於算電阻R的時候有一些問題,當電阻為0時還是傳回了1/R,但是這次我巧妙的避開了這個問題,導致下一次迭代在這裡栽了大跟頭,果然寫程式還是不能對bug視而不見啊

第八次pta作業

對於這次的pta我還是有很多東西要說的,因為這是寫的最艱難的一次,甚至最後

關於類設計

1.Content題目類

2.Paper試卷類

3.Answer答卷類

4.Main主類

分析程式碼

1.對於正規表示式,與上一題差不多,都是用正規表示式對題目進行分組提取有用的部分,但是這次較為不同的是試卷和答卷中的資訊不確定,所以正規表示式有所改變,比如:

想要匹配T中的題目編號及分值,但是不知道個數,所以我使用了兩次搜尋,從而達到目的

點選檢視程式碼
Pattern pattern1 = Pattern.compile("#T:\\s*(\\d+).+");
        Matcher matcher1 = pattern1.matcher(all);
        Paper[] paper = new Paper[100];
        for(int i=0;i<100;i++){
            paper[i]=new Paper();
        }
        int c=0;
        int[] a=new int[100];
        int d=0;
        while(matcher1.find()){//先找到#T這句話
            String[]temp = new String[100];
            String[]temp1 = new String[100];
            d=0;
            Pattern pattern2 = Pattern.compile("(\\d+)-(\\d+)");
            Matcher matcher2 = pattern2.matcher(matcher1.group(0));//在#T這句話中再次進行匹配,尋找後面想要的部分
            while(matcher2.find()){
                temp[d]=matcher2.group(1);
                temp1[d]=matcher2.group(2);
                d++;
                a[Integer.parseInt(matcher1.group(1))]++;
            }
            paper[c]=new Paper(matcher1.group(1),content,temp,temp1);
            c++;
        }

對於#S的資訊提取也是同理
2.關於耦合度,這次依然使用了關聯類,所以其他類之間耦合度較高

可以看到將輸出等多個方法都放在了Paper類中,是不合理的,後期應該將方法分別放到其他類中
由於產生了迭代,加了一直方法,導致圈複雜度較上一題要大

3.關於第二次pta作業的一個小題目,那個題提到了介面,於是我就去查了一下,發現介面可以彌補繼承無法多繼承的缺陷

踩坑心得

1.對於題目的理解出了一點偏差,導致有一個測試點一直過不去,題目是如果答卷沒有對應的試卷輸出“The test paper number does not exist”,但是我理解成了試卷找不到答卷輸出這句話,導致最後一個測試點一直過不去,難受啊
改之前是這樣

改之後的程式碼是這樣

2.對於題目,我一開始是建立陣列,讓陣列中的資訊存在相應序號的一個陣列元素中,導致老是報NullPoint,而且會使後期的輸出變得非常麻煩,所以最終決定重新寫,也就成了現在的版本
之前是把序號當陣列下標使用,所以會有空陣列出現
3.建議寫題目應該先畫一個類圖,把類之間的關係理清楚,不然寫的時候會很混亂,使程式碼變得複雜

改進建議

1.對於第二次的程式碼總體覺得尚可,下次的題目可以在這次的基礎上進行類的增加,前面的邏輯依然是正確的
2.在類的設計上依然有不足,Paper類不應該與其他類相關聯,應該重新設計一個類使各個類之間產生聯絡

第三次pta作業

第二個小題

關於次此次作業的第二個小題,我覺得還是可以說一下,關於日期類的使用,倒是一個好東西,可以幫助我們快速處理日期問題,關於使用如下:
先用LocalDate定義一個物件,之後就可以使用其方法,如果想要知道日期是一年中的第幾天可以用getDayOfYear方法,月中第幾天用的是getDayOfMonth,一週中的第幾天用的是getDayOfWeek方法,但是要注意的是,getDayOfWeek返回的是星期幾的英文,所以需要使用getValue轉化成數字,還可以使用date1.until(date2, ChronoUnit.DAYS)來求date1和date2相差多少天,大概用到的就這些方法,在此簡述一下

關於類設計

1.Answer答卷類

class Answer {//答卷類
	private int paperNum;//答卷號
	private Answer1[] answer = new Answer1[100];//答卷中的答案
	private String studentID;//答卷學生的學號
}

2.Answer1答案類(用這個名字純屬因為Answer前面被答卷類佔用了)

class Answer1 {//答案類

	private String num;//答案序號
	private String answer;//答案內容
}

3.Content題目類

class Content {//題目類

	private String num;//題目序號
	private String content;//題目內容
	private String standardAnswer;//標準答案
	private boolean isDelete = true;//判斷此題目是否被刪除
}

4.Control控制類

class Control {//控制類,將類之間建立聯絡

	private Content[] content = new Content[100];//題目
	private Answer[] answer = new Answer[100];//答卷
	private Paper[] paper = new Paper[100];//試卷
	private Student[] student = new Student[100];//學生
	private Delete[] delete = new Delete[100];//刪除資訊
}

其中有一些重要的方法

點選檢視程式碼
public void sumGrade(int j) {//判讀試卷中總分是否有100分
		int sum = 0;
		for (int i = 0; i < 100; i++) {
			if (paper[j].getContentPoint()[i] != null) {
				sum = Integer.parseInt(paper[j].getContentPoint()[i]) + sum;
			} else {
				break;
			}
		}
		if (sum != 100) {
			System.out.println("alert: full score of test paper" + paper[j].getNum() + " is not 100 points");
		}
	}
public void println(int g, int j, int a, int e) {//輸出函式,主要邏輯在這
		int f = 0;
		int[] flag = new int[100];
		int sum = 0;
		for (int i = 0; i < a; i++) {
			f = 0;
			for (int h = 0; h < 100; h++) {
				if (answer[j].getAnswer()[h] != null) {
					if (Integer.parseInt(answer[j].getAnswer()[h].getNum()) == i + 1) {
						f = 1;
						break;
					}
				}
			}
			if (f == 0) {
				System.out.println("answer is null");
			} else {
				if (content[Integer.parseInt(paper[g].getContentNum()[i])] == null) {
					System.out.println("non-existent question~0");
				} else {
					if (content[Integer.parseInt(paper[g].getContentNum()[i])].isDelete() == false) {
						System.out.println(
								"the question " + Integer.parseInt(paper[g].getContentNum()[i]) + " invalid~0");
					} else {
						for (int k = 0; k < 100; k++) {
							if (answer[j].getAnswer()[k] != null) {
								if (Integer.parseInt(answer[j].getAnswer()[k].getNum()) == i + 1) {
									System.out
											.print(content[Integer.parseInt(paper[g].getContentNum()[i])].getContent());
									if (answer[j].getAnswer()[k].getAnswer()
											.equals(content[Integer.parseInt(paper[g].getContentNum()[i])]
													.getStandardAnswer())) {
										System.out.println("~" + answer[j].getAnswer()[k].getAnswer() + "~" + "true");
										flag[i] = 1;
									} else {
										System.out.println("~" + answer[j].getAnswer()[k].getAnswer() + "~" + "false");
										flag[i] = 0;
									}
								}
							}
						}
					}
				}
			}
		}
		int flag1 = 0;
		for (int i = 0; i < e; i++) {
			if (answer[j].getStudentID().equals(student[i].getStudentID())) {
				flag1 = 1;
				System.out.print(student[i].getStudentID() + " " + student[i].getName() + ": ");
			}
		}
		if (flag1 == 1) {
			for (int i = 0; i < a; i++) {
				if (paper[g].getContentPoint()[i] != null) {
					if (i != a - 1) {
						if (flag[i] == 1) {
							sum += Integer.parseInt(paper[g].getContentPoint()[i]);
							System.out.print(paper[g].getContentPoint()[i] + " ");
						} else {
							System.out.print("0" + " ");
						}
					} else {
						if (flag[i] == 1) {
							sum += Integer.parseInt(paper[g].getContentPoint()[i]);
							System.out.print(paper[g].getContentPoint()[i]);
						} else {
							System.out.print("0");
						}
					}
				} else {
					System.out.print("0");
				}
			}
			System.out.println("~" + sum);
		} else {
			System.out.println(answer[j].getStudentID() + " not found");
		}
	}
public void isWrongFormat(ArrayList<String> all) {//判斷輸入的格式是否錯誤
		for (int i = 0; i < all.size(); i++) {
			Pattern pattern = Pattern.compile(
					"^#N:(\\d+) #Q:(.*?) #A:(.*)|^#T:\\s*(\\d+)(\\s*\\d+\\-\\d+)*$|^#S:(\\d+) (#A:\\s*(\\d+)\\s*-\\s*(.*?)\\s*(?=#A:|\\n|$))*|^#X:((\\d+) (\\w+)-*)+|^#D:N-(\\d+)$");
			Matcher matcher = pattern.matcher(all.get(i));
			if (matcher.find()) {

			} else {
				System.out.println("wrong format:" + all.get(i));
			}
		}
	}
public void isDelete(int f) {//判斷題目是否被刪除
		for (int i = 0; i < 100; i++) {
			if (content[i] != null) {
				for (int j = 0; j < f; j++) {
					if (delete[j].getContentNum().equals(content[i].getNum())) {
						content[i].setDelete(false);
					}
				}
			}
		}
	}
}

分析程式碼

1.這次的迭代導致我加了很多的類,使類間關係變得比前兩個要複雜很多

此次主要把輸出之類的方法單獨寫了一個類出來,讓其他類之間沒有關聯,用Control類來聯絡其他的類
2.此次的輸出邏輯有所變化,上次是答案序號就是試卷中題目出現的順序,直接和題目相聯絡,但是這次加上了答案序號,是答案可以亂序,導致輸出邏輯有所變化
3.這次的輸出還有一個優先順序的問題,當一道題目同時出現答案不存在、引用錯誤題號、題目被刪除,只提示一種資訊,答案不存在的優先順序最高,所以我就將答案不存在當做第一輸出,若答案存在,再去判斷錯誤題號和題目被刪除


先輸出Answer is null!,之後在判斷其他
4.這次的輸入和上次的沒有很大的區別,但是新增了錯誤格式判斷,讓我措手不及,由於對正規表示式認識不足,導致改了好幾天,還是一份沒漲的 奇蹟 有趣事件
5.下面是SourceMonitor的測試結果

可以看到圈複雜度要比上面兩題大很多,這次迭代的程式碼量也較上兩題有很大提升

踩坑心得

1.關於這次的題目踩坑心得,我認為首當其衝的就是正規表示式上的坑點,畢竟可以改了很久,一直以為是邏輯有不足,結果改到最後也基本都是正規表示式上的問題

public void isWrongFormat(ArrayList<String> all) {//判斷輸入的格式是否錯誤
		for (int i = 0; i < all.size(); i++) {
			Pattern pattern = Pattern.compile(
					"^#N:(\\d+) #Q:(.*?) #A:(.*)|^#T:\\s*(\\d+)(\\s*\\d+\\-\\d+)*$|^#S:(\\d+) (#A:\\s*(\\d+)\\s*-\\s*(.*?)\\s*(?=#A:|\\n|$))*|^#X:((\\d+) (\\w+)-*)+|^#D:N-(\\d+)$");
			Matcher matcher = pattern.matcher(all.get(i));
			if (matcher.find()) {

			} else {
				System.out.println("wrong format:" + all.get(i));
			}
		}
	}

2.關於正規表示式的坑,首先由於題目需要判斷錯誤格式,有的錯誤格式是在輸出前加了一些東西,而針對這個錯誤格式我是使用了開始符號,“^”,可以達到以想要的格式輸入和判斷錯誤格式,有個同學用的是長度判斷,感覺這個好像也挺好

但是這個就讓我之前的邏輯出了大問題,因為我以前的邏輯是先把每一句話都存在了一個字串裡,導致加了開始符會找不到正確的格式,所以後面又把輸入給改了(悲)

3.對於輸入時每一句輸入的儲存,我一開始是使用了普通的陣列,導致每一種型別的輸入我都要去搞一個變數來計數,去當後面迴圈遍歷輸出時迴圈的限制條件,所以後面又進行了一次小改動,將一些陣列改成了動態陣列,這樣就可以呼叫其size方法來獲得後期輸出的迴圈限制條件
4.關於一些測試點的坑,空白試卷,一開始我的正規表示式對此判斷的是錯誤格式

之後對於正規表示式進行了修改

還有後面的答案為空字元測試點,一直過不了,之後又學習了關於正規表示式的前瞻斷言和非貪婪模式搜尋,將這個測試點過了
後面還有一個比較坑的點是題目竟然可以不只算數題,標準答案也可以為空或者是一句話例如go to,所以對標準答案的正規表示式也有問題,之後又進行修改

也是終於得到滿分

改進建議

1.關於對有用資訊的提取部分,不應該直接在主類中進行,後面要建立一個類將其封裝,使後面加上繼承和多型的時候不至於程式碼全錯
2.關於輸出函式和試卷是否滿分等方法個人認為只有一個Control來做依然不是很好,讓程式碼的複用性很低,後期應對其進行修改

總結

學習到的內容

1.正規表示式

關於正規表示式方面我認為收穫還是很大的,正規表示式可以對有用資訊進行提取,而本次題集我主要是使用了對匹配資訊進行分組來達到提取有用資訊進行輸入的目的,有同學用的是split對資訊進行切割,感覺兩種方法都挺好,都可以對有用資訊進行提取,還有正規表示式的開始符號,不能將資訊都放在一個字串中使用,不然就會出錯,正規表示式的非貪婪匹配(.*?)和前瞻斷言(?=),可以解決空字元的問題,這兩個東西都非常好用,但是對於正規表示式還是有很多東西不夠熟練,希望在後期的訓練中一點一點積累,最終能熟練掌握正規表示式

2.類設計

這幾次的題目集都是在訓練我們的類設計能力,但是主要還是考察了類的單一職責原則,就是讓類的功能單一化,提高程式碼的可複用性,為後續迭代做準備,經過三次的pta練習對於類的單一職責我有了初步的認識,但是這三次的類設計並不夠好,需要進行更改,為下一次的迭代做準備
總的來說,個人認為老師們的教學方法還是很好的,可以學到很多東西,雖然過程比較艱辛,但是收穫很多,可以學到很多知識
關於oop的第一次blog就到此結束