面對對象程式設計 第十到十六周作業總結 引言:java的課程進入尾聲,但是編程的路才剛開始。 前言:這三周的大作業主要考察各個類之間的關係,數據的封裝,類的繼承,多態,介面,抽象類,集合框架等多個知識的綜合運用。 自學正則表達式的使用,有些題目對於格式的判斷非常的複雜,使用正則表達有效判斷了輸入的合 ...
面對對象程式設計---第十到十六周作業總結
引言:java的課程進入尾聲,但是編程的路才剛開始。
前言:這三周的大作業主要考察各個類之間的關係,數據的封裝,類的繼承,多態,介面,抽象類,集合框架等多個知識的綜合運用。
自學正則表達式的使用,有些題目對於格式的判斷非常的複雜,使用正則表達有效判斷了輸入的合法性,並且減少了大量的格式判斷代碼。
通過三次大作業寫完電信計費問題,這樣的方式相比之前的點線型友好了太多,這樣就又充分的時間去書寫代碼,完成代碼的內容。
三次大作業彼此關聯難度又呈持平的狀態,給我們編碼時提供了莫大的信心。
PTA大作業6
各題目 設計與分析 踩坑心得 改進意見 核心代碼分析:
(1)7-1 電信計費系列1-座機計費
設計與分析:
類圖如下:
此題的難度在於不好下手,做此類圖較為複雜的題,需要仔細觀察類圖,明確類圖之間的聯繫
在此題中 User類中的userRecord是這道題目的突破口,在這個userRecord中可以把用戶的通話記錄數據放入,便於Chargemode在中間的調用。
然後再去理解類圖中的chargeMode類,這是這個程式的核心代碼,通過這一段計算出用戶的花費,完成程式。
雖然類圖看起來非常的複雜,但只要將代碼分解,先從user入手,自然就寫到了userRecord,接著就補充到chargemode,再然後就去構思main函數,獲取輸入數據即可
踩坑心得:
這應該是我最重要的一部分代碼,看起來非常的簡單,但得來是十分不容易的。
這道題目涉及到對arrayList排序的問題。我開始是先聲明一個User user[]的對象數組,先歷便arrayList,將數據賦值給user[],
再用compareTo方法對字元串進行冒泡排序,但是排序之後就出現了一個問題,我通過user數組賦值沒有辦法刪除重覆的元素,這就導致輸出重覆。
雖然最後解決了重覆的問題,但是這種方法效率比較低,我又找到了以上方法。
在把數據比較入arrayList時進行,如果arratList的user.numble值比後一個要大,那麼用j記錄下arrayList中的位置,再用指定位置插入,將新進來的數字插入進來,這樣插入完成後就已經是有序的了。
改進意見:
1.由上圖可知,這個代碼的圈複雜度達到了19,這說明代碼的質量不高。因為我在寫題目的過程中,只註意需要完成題目要求的功能,而沒有對代碼進行優化,導致寫了過多的if/else語句,據查找資料,if/else 和迴圈的使用會使得圈複雜度提高,改進的方法是將if/else語句換成switch語句,可以有效減少圈複雜度。
2.可以將判斷的邏輯顛倒過來,這樣代碼的思路就會更加的清晰,代碼的可讀性也會更高。
3.可以將arrayList換成hashset,用集合簡化題目。
核心代碼分析:
① 數據判斷的正則表達式:
final String regex = "^u-[0-9]{11,12} 0$"; final String regex0 = "^t-[0]{1}[0-9]{9,11} [0]{1}[0-9]{9,11} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$"; final String regex00 = "^079[0-9]$";
②arrayList的排序演算法:
while(!s.equals("end")) { if(Pattern.matches(regex, s)) { String s0 = s.substring(2); String str[] = s0.split("[ ]"); int flag=0; User user = new User(); user.setNumber(str[0]); for(User i:list){ if(i.getNumber().equals(user.getNumber())) flag=1; } int j=0; for(User m:list) { if(m.getNumber().compareTo(user.getNumber())>0) break; j++; } if(flag==0){ list.add(j,user); } }
7—2電信計費系列2-手機+座機計費
各題目 設計與分析 踩坑心得 改進意見 核心代碼分析:
(1)7-2 串口字元解析
設計與分析:
類圖如下(其他與上一題基本相同)
此題的難點在於對於手機來說:撥打電話和接聽電話都需要計費,不但需要判斷是撥打還是接聽,還需要判斷所在的位置。
對於接聽來說,所在的位置如果發生變化,相應的計費金額也會有所改變。
核心就來到了判斷輸入是否合法,輸入的時間是否合法等問題,其意思是說如過無法判斷輸入數據的準確與否,就無法寫出這道題目。
我同樣採取了正則表達式判斷時間,對於本題給出的格式yyyy-MM-dd HH:mm:ss。我寫了一個簡易的時間正則表達式。
在獲得輸入的數據後需要瞭解計費規則的相關類,
這些類的核心方法是: calCost(ArrayList<CallRecord> callRecords)。 該方法針根據輸入參數callRecords中的所有記錄計算某用戶的某一項費用;
如市話費。 輸入參數callRecords的約束條件:必須是某一個用戶的符合計費規則要求的所有記錄。 SendMessageRule是發送簡訊的計費規則類,用於計算發送簡訊的費用。
踩坑心得:
這一部分的代碼就是五個部分的判斷,對於題目給出的五個地區不同的區號判斷。
只需要從地區範圍由小到大判斷,就不會遺漏數據了。
改進意見:
1、由上圖分析,這道題的圈複雜度為70,超過了15,這表明代碼的質量還可以提高,這道題是由於寫了很多個迴圈的結果導致。其實有些迴圈可以把他合併起來,這樣既減少了代碼的行數,又提高了代碼的質量。
2、這道題存在有些代碼重覆率過高的問題,可以通過靜態方法的調用,解決這些代碼的重覆問題。
核心代碼分析:
①時間的正則表達式:
final String landtel = "t-[0]{1}[0-9]{9,11} 1[0-9]{10} [0-9]{3,4} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$"; // 座機--電話 final String teltel = "t-1[0-9]{10} [0-9]{3,4} 1[0-9]{10} [0-9]{3,4} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$"; final String telland = "t-1[0-9]{10} [0-9]{3,4} 0[0-9]{9,11} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$";
②arrayList的使用:
if (Pattern.matches(regextel, s)) { String s0 = s.substring(2); String str[] = s0.split("[ ]"); int flag = 0; User user = new User(); user.setNumber(str[0]); for (User i : listtel) { if (i.getNumber().equals(user.getNumber())) flag = 1; } int j = 0; for (User m : listtel) { if (m.getNumber().compareTo(user.getNumber()) > 0) break; j++; } if (flag == 0) { listtel.add(j, user); } } } else if (Pattern.matches(regex0, s) || Pattern.matches(landtel, s)) {// 座機通話記錄 座機打座機 // 增加座機打手機 if (Pattern.matches(regex0, s)) { UserRecords ue = new UserRecords(); CallRecord callRecord = new CallRecord(); String str[] = s.split("[ ]"); String d1 = str[2] + " " + str[3]; String d2 = str[4] + " " + str[5]; Date da1 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").parse(d1); Date da2 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").parse(d2); callRecord.setCallingNumber(str[0].substring(2)); callRecord.setCallingAddressAreaCode(str[0].substring(2, 6)); callRecord.setAnswerNumber(str[1]); callRecord.setAnswerAddressAreaCode(str[1].substring(0, 4)); callRecord.setStartTime(da1); callRecord.setEndTime(da2); for (User j : list) { if (j.getNumber().equals(callRecord.getCallingNumber())) { ue = j.getUserRecords(); } else continue; if (callRecord.getAnswerAddressAreaCode().equals(callRecord.getCallingAddressAreaCode())) { // 判斷地區 ue.addCallingInCityRecords(callRecord); } else if (callRecord.getAnswerAddressAreaCode().equals("0701") || Pattern.matches(regex00, callRecord.getAnswerAddressAreaCode())) { ue.addCallingInProvinceRecords(callRecord); } else ue.addCallingInLandRecords(callRecord); j.setUserRecords(ue); break; } } if (Pattern.matches(landtel, s)) { UserRecords ue = new UserRecords(); UserRecords ue0 = new UserRecords(); CallRecord callRecord = new CallRecord(); s = s.substring(2); String str[] = s.split("[ ]"); String d1 = str[3] + " " + str[4]; String d2 = str[5] + " " + str[6]; Date da1 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").parse(d1); Date da2 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").parse(d2); callRecord.setCallingNumber(str[0]); callRecord.setCallingAddressAreaCode(str[0].substring(0,4)); callRecord.setAnswerNumber(str[1]); callRecord.setAnswerAddressAreaCode(str[2]); callRecord.setStartTime(da1); callRecord.setEndTime(da2); // 分區域放進去 for (User j : list) { if (j.getNumber().equals(callRecord.getCallingNumber())) { ue = j.getUserRecords(); } else continue; if (callRecord.getAnswerAddressAreaCode().equals(callRecord.getCallingAddressAreaCode())) { // 判斷地區 ue.addCallingInCityRecords(callRecord); } else if (callRecord.getAnswerAddressAreaCode().equals("0701") || Pattern.matches(regex00, callRecord.getAnswerAddressAreaCode())) { ue.addCallingInProvinceRecords(callRecord); } else ue.addCallingInLandRecords(callRecord); j.setUserRecords(ue); break; }
7-1 電信計費系列3-簡訊計費
各題目 設計與分析 踩坑心得 改進意見 核心代碼分析:
(1)7—1 計算兩點間的距離
設計與分析:
這題只需要用一個String的字元串去接收題目中的輸入,再使用String中的方法spilt將字元串進行拆分
本題的難度在於判斷輸入的簡訊字元段,同意採用正則表達的,在計算字元串的長度就可以了。
踩坑心得:
實現這道題目主要就是截取到簡訊,在使用之前類似的方法就可以
改進建議:
1、由圖可知,該題的圈複雜度為17。這裡有點可惜,如果小於10代碼質量就會很好。改進可以減少迴圈的使用。
2、這題可以將獲取字元串變成一個字元數組。、此題可以將split方法改進,提高程式的執行效率
核心代碼分析:
①正則表達式:
final String regex = "^u-[0-9]{11,12} 0$";// 座機開戶u-[] 0;正則 final String regex0 = "^t-[0]{1}[0-9]{9,11} [0]{1}[0-9]{9,11} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$"; // 座機撥打電話格式正則 final String landtel = "t-[0]{1}[0-9]{9,11} 1[0-9]{10} [0-9]{3,4} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$"; // 座機--電話 final String teltel = "t-1[0-9]{10} [0-9]{3,4} 1[0-9]{10} [0-9]{3,4} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$"; final String telland = "t-1[0-9]{10} [0-9]{3,4} 0[0-9]{9,11} [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|(3[0-1])) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9]) [0-9]{4}[.](([1-9]{1})|([1]{1}[0-2]{1}))[.]([1-9]|([1-2]{1}[0-9]{1})|3[0-1]) (([0-1][0-9])|(2[0-3]))[:]([0-5][0-9])[:]([0-5][0-9])$"; final String regex00 = "^079[0-9]$";// 座機區號正則 final String regextel = "u-1[0-9]{10} 1$";// 手機開戶正則 final String regexmessage= "u-1[0-9]{10} 3$"; final String regexmess = "m-1[0-9]{10} 1[0-9]{10} ([\\w]|[ ]|[.]|[,])+$";
②簡訊計費:
class SendMessageRule extends MessageChargeRule{ double calCost(ArrayList<MessageRecord> messageRecords) { int n=0; for(int i=0;i<messageRecords.size();i++) { if(messageRecords.get(i).getMessage().length()<=10) { n++; } else { if(messageRecords.get(i).getMessage().length()%10==0) n += messageRecords.get(i).getMessage().length()/10; else n += messageRecords.get(i).getMessage().length()/10+1; } } if(n<=3) return n*0.1; else if(n<=5) return 0.3+(n-3)*0.2; else return 0.7+(n-5)*0.3; } }
實驗四
設計與分析:
實驗4(1)主要是理解看懂給出的類圖,根據類圖寫出代碼。理解各種抽象類之間的聯繫,最難的應該是GaneDate類型的理解。這個類把這幾個物品之間聯繫起來,實現游戲的功能,這幾個類圖之間的代碼實現
實驗4(2)在原有的基礎上把boat類繼承到abstractObjectial即可。把abstractransport變成介面,再用boat實現這個介面,這道題就算寫完。
在代碼實現過程中遇見的最大問題就是看懂題給出的類圖,寫出類圖這道題目就相當於完成了一大半,在這個基礎上把各個類串在一起,理解每個類之間的關係,就可以完成這道題。為了避免修改農夫過河前幾次代碼中代碼的主幹,我在游戲類中保留了這些代碼,這樣的代碼修改難度就降低了不少,最後運行完成後debug找出了一個邏輯上的錯誤,就提交了這次實驗。
這是case31錯誤時的截圖,這道題這個點我也是測試了非常久才給出了正確的答案。
原因在於一個函數
實驗4(1)主要是理解看懂給出的類圖,根據類圖寫出代碼。理解各種抽象類之間的聯繫,最難的應該是GaneDate類型的理解。這個類把這幾個物品之間聯繫起來,實現游戲的功能,這幾個類圖之間的代碼實現
實驗4(2)在原有的基礎上把boat類繼承到abstractObjectial即可。把abstractransport變成介面,再用boat實現這個介面,這道題就算寫完。
在代碼實現過程中遇見的最大問題就是看懂題給出的類圖,寫出類圖這道題目就相當於完成了一大半,在這個基礎上把各個類串在一起,理解每個類之間的關係,就可以完成這道題。為了避免修改農夫過河前幾次代碼中代碼的主幹,我在游戲類中保留了這些代碼,這樣的代碼修改難度就降低了不少,最後運行完成後debug找出了一個邏輯上的錯誤,就提交了這次實驗。
改進意見:
1、如複雜度分析圖所示,本題的圈複雜度為33,超過了代碼的一般範圍,這就意味這該代碼的可讀性非常的差,而對與前文提到的方法都可有效減少圈複雜度。
2、本題的代碼有兩百多行,代碼的簡化就顯得尤為重要,應該適當的合適代碼的邏輯,讓代碼模塊化。
3、如果能夠將代碼的邏輯顛倒一下,程式將更加的合理。
核心代碼如下:
public abstract class AbstracTransport { private String place ="a";public String departure ="a"; private int capacity; private ArrayList<MaterialObject>goodses = new ArrayList<MaterialObject>(); abstract public void moveTo( String destination); public int getCapacity() { return capacity; } public void setCapacity(int capacity) { this.capacity = capacity; } public String getPlace() { return place; } public void setPlace(String place) { this.place = place; } public ArrayList<MaterialObject> getGoodses() { return goodses; } public void setGoodses(ArrayList<MaterialObject> goodses) { this.goodses = goodses; } } class Boat extends AbstracTransport{ public void moveTo(String destination) { if(this.getPlace() == destination) setPlace(departure); else setPlace(destination); } public void crossRiver(Person person) { if(person.isPlace()) person.setPlace(departure); else person.setPlace("b"); } public void crossRiver(Person person, MaterialObject m) { this.crossRiver(person); if(m.isPlace()) m.setPlace(departure); else m.setPlace("b"); } public void broad(MaterialObject m) { getGoodses().add(m); } public void disembark(MaterialObject m){ getGoodses().remove(m); } } public abstract class AbstractGame { private AbstractRule gameOverRule = new GameOverRule(); private AbstractRule gameSuccessRule = new GameSuccessRule(); private GameData gameDate = new GameData(); public abstract void play() ; } public abstract class AbstractRule { public abstract boolean judge(GameData gameData); } class CrossRiverRule extends AbstractRule{ public boolean judge(GameData gameData) { if(hasCross(gameData.cabbage)&&hasCross(gameData.sheep)&&hasCross(gameData.wolf)&&hasCross(gameData.farmer)) return true; return false; } public boolean hasCross(MaterialObject m) { if( m.getPlace().equals(m.destination)) return true; return false; } } class ObjectExistRule extends AbstractRule{ public boolean judge(GameData gameData){ if(gameData.sheep.isExist()&&gameData.wolf.isExist()&&gameData.cabbage.isExist()) return true; return false; } } class GameOverRule extends AbstractRule{ ObjectExistRule oe= new ObjectExistRule(); CrossRiverRule cr = new CrossRiverRule(); public boolean judge(GameData gameData) { if(!oe.judge(gameData)) return true; if(cr.judge(gameData)) return true; return false; } }