1. 程式人生 > 資訊 >雷軍成立金山奇文企業管理諮詢公司,持股 100%

雷軍成立金山奇文企業管理諮詢公司,持股 100%

(1)前言:

  題目集7-9題目量不大,第7次2題(圖形卡片排序、分組),第8次、第9次各為1題(ATM機類結構設計)。題量不大,但難度較高(尤其是後兩次題目集)。這三次的題目集難度並不在演算法上,而是難在知識點,如實現多型(這三次題目集大量使用),介面(在這四道題目僅僅是用到,未深入),還有儘可能實現開閉原則和單一職責原則。題目集7-9的知識點有介面、多型、繼承、ArrayList的使用、Collections(排序)的使用、Iterator(迭代器)的使用、開閉原則和單一職責原則

(2)設計與分析:

  題目集7(7-1):解題思路:先構建好各個圖形類,然後再將輸入的代表圖形的數字一一用ArrayList儲存,儲存前判斷輸入的數字是否正確,若正確,進行下一步,反之輸出錯誤。再根據儲存的數字把相對應的資料進行計算並將對應的圖形作為物件存入ArrayList裡(當然,存入之前已經是確認資料無誤的)。

  以下是生成報表:

  由圖可知我這一題的程式碼平均複雜度即深度還算正常,即使是Triangle類有7的複雜度,仍然在正常範圍內。就是DealCardList類的平均複雜深度嚴重超標(或許是呼叫的物件過多,但這不可避免)。

  類圖如下:

  由圖可知,Main類呼叫DealCardList類,DealCardList類呼叫Card類,Card類和Shape類是聚合關係。Circle、Rectangle、Triangle、Trapezoid四個類均繼承於Shape類。DealCardList類是一個工具類,用於儲存各圖形資料,和檢測各資料的合理性。

  題目集7(7-2):解題思路:和題目集7(7-1)類似,就是比7(7-1)多了多個相同圖形面積排序和總面積排序,通過建立多個ArrayList動態陣列來實現分別儲存相同圖形的資料。

  以下是生成報表:

  

  由圖可知,我這道題的程式碼平均複雜度和深度還行(除了DealCardList類)。DealCardList類平均複雜度高的原因是用的大量的for-each語句來遍歷相加,以及圖形面積值的輸出。

  類圖如下:

  由圖可知,7(7-2)和7(7-1)的類圖相似,類與類之間的關係以及類的作用幾乎沒有變化的,相同部分就不再做分析。

  題目集7(7-1)、(7-2)兩道題目的遞進式設計分析總結:

  題目集7(7-1)很明顯是為7(7-2)做鋪墊的。7(7-1)的要求就是把圖形卡片分類,計算各自的面積值,再根據面積值的大小進行排序。在輸出的時候先輸出排序前的面積(即按使用者輸入的順序為準),再輸出排序後的面積,最後輸出圖形總面積。7(7-2)

的要求是把圖形卡片分類,計算各自的面積值,再根據同類圖形的面積值大小進行排序。在輸出的時候同類圖形先輸出排序前的面積,再輸出同類圖形排序後的面積,最後輸出最大圖形總面積(每一類的圖形不止一個時,求它們的和)。輸出參考結果如圖:

7(7-1)

7(7-2)

  這兩個參考輸出結果比較圖可以明顯地看出相比於7(7-1),7(7-2)的格式要求和排序方法是基於7(7-1)而來的。排序方法並沒有變,僅僅是改動了輸出格式。7(7-1)更像是在先教會如何運用物件動態陣列(就像題目中的ArrayList<Card>),等到理解後就開始把圖形分類,由把“圖形”作為分類的依據,改為“圖形種類”作為分類的依據。這個遞進,我是用多加幾個ArrayList來實現的(上面放的類圖明顯可以看出,多了4個物件動態陣列)。

  注:7(7-1)、7(7-2)兩題的排序均為降序排序。

  題目集8(7-1):解題思路:先根據題目給的條件,按照題目的要求構建了實體類和自行添加了業務類。為實現功能,我寫了兩個“套娃鏈”,分別是“銀聯--銀行--ATM機”和“使用者--銀行--賬戶--卡號”(比如銀聯有多個銀行,每個銀行有多個ATM機,用ArrayList實現)。然後根據缺少的我添加了初始化類和代理類兩個業務類。初始化類用於初始化資料並存儲,代理類則處理一切取款業務(包含非法輸入判斷)。

  以下是生成報表:

  由圖可知,Agent類和Initialize類平均複雜度和深度較高。是因為Initialize類在資料儲存的時候用了一些for-each迴圈語句,導致複雜度過高。Agent類是因為我把判斷和取款都放在了同一個類裡,判斷是否為正確輸入至少需要5個if,而取款的方法又要用到迭代器遍歷,while巢狀、if巢狀不可避免。複雜度和深度自然就高了。

  類圖如下:

  由圖可知,Main類調了Agent類和Initialize類,Agent類和Initialize類均呼叫了所有實體類(ChinaUnionPay、Bank、ATM、User、Account、Card六個類),Bank類裡有一個以ATM為型別的動態陣列,User類裡有一個以Account為型別的動態陣列,Account類裡有以Card為型別的動態陣列。業務類Agent類有校驗和取款功能,Initialize類有初始化資料的功能。

  題目集9(7-1):解題思路:題目要求在上一次的基礎上新增貸款和跨行取款功能。題目說明說了卡能否貸款取決於賬戶。我就把Account類改成了AbstractAccount類(抽象類),再用CreditAccount類(貸記賬戶類)和DebitAccount類(借記賬戶類)分別繼承於AbstractAccount類。DebitAccount類是和8(7-1)的Account功能一樣,CreditAccount類則是在Account類的基礎上變動了可貸款餘額(若檢測到時貸記卡才會增加貸款餘額)。貸款功能的實現是在Card類里加了卡的種類。跨行取款就是在初始化資料的時候在Bank類里加了跨行手續費比例。

  以下是生成報表:

  由圖可知,Main類的語句數特多。語句多則是我把初始化放在了Main類裡面。而且初始化有很多語句。ValidateData類的複雜度和語句數較多,是因為判斷資料正誤的話if不可少,而且判斷的東西也多,所以語句數就多了。至於Withdraw類我判斷了是否跨行,因為跨行的演算法和不跨行有一些差別,所以導致最大複雜度稍高。

  類圖如下:

  由圖可知,所有實體類(ChinaUnionPay、Bank、ATM、User、Account、Card六個類)除了ChinaUnionPay類以外,都實現了雙向連結串列,即可以相互查詢(如通過賬戶可以得知使用者,通過使用者也可以得知賬戶)。GetBalance類用於獲取賬戶餘額,ValidateData類用於判斷資料正誤情況,Withdraw類用於取款。

  題目集8和題目集9兩道ATM機模擬題目的設計思路分析總結:

  這兩道題都有一個特點,就是類比較多。因此,每個類的功能都更加細化。8(7-1)的設計方面有兩個難點,一是資料的初始化,二是如何實現從下而上的查詢(如給賬戶找擁有者)。而且在題目說明裡明確說了業務類自行新增。也就是說,像實體類只能進行新增(set、add)、刪除(remove)、獲取(get)的操作,而對資料進行進一步操作就必須要用業務類了。其實8(7-1)從下而上的查詢是解決此題的關鍵。使用Iterator(迭代器)和多次while迴圈,可以進行遍歷所有資料進行匹配,最終判斷資料是否存在。9(7-1)的設計方面難點在於如何實現多型。題目說明著重寫了卡能否貸款取決於賬戶型別,所以賬戶是肯定要繼承於抽象類的,同時也便於再新增新型別的賬戶。而兩種賬戶唯一的差別就在於能否貸款,所以可以針對能否貸款這一點來構建抽象類。至於跨行手續費(銀行),和貸款手續費(銀聯)就只是在相應類裡面加一個屬性的事。

(3)採坑心得:

  7(7-1):一開始我沒注意Scanner要用靜態,導致後面資料的輸入出現了問題,在除錯過程中發現後面圖形卡片與圖形相對應的資料不能一一輸入並存儲,總是會在存了一個圖形卡片後停下來。下面程式碼中註釋掉的地方就是檢測是否執行到了相應case裡面的語句。

 1 public DealCardList(ArrayList<Integer> choice) {
 2        // System.out.println(i);
 3         for(int i : choice) { 
 4         
 5 //             System.out.println(choice.get(i));
 6             switch(i) {
 7                 case 1:{
 8                  //   System.out.println("1");
 9                     double radius = Main.in.nextDouble();
10                     Circle cir = new Circle(radius);
11                     Card card1 = new Card(cir);
12                     list.add(card1);
13                  break;
14                  }
15                 case 2:{
16 //                     System.out.println("2");
17                     double width = Main.in.nextDouble(),length= Main.in.nextDouble(); 
18                     Rectangle rec=new Rectangle(width, length);
19                     Card card2 = new Card(rec);
20                     list.add(card2);
21                  break;
22                 }
23                 case 3:{
24 //                     System.out.println("3");
25                     double a = Main.in.nextDouble(),b= Main.in.nextDouble(),c= Main.in.nextDouble(); 
26                     Triangle tri = new Triangle(a,b,c);
27                     Card card3 = new Card(tri);
28                     list.add(card3);
29                  break;    
30                 }
31                 case 4:{
32 //                     System.out.println("4 "+i);
33                     double a = Main.in.nextDouble(),b= Main.in.nextDouble(),h= Main.in.nextDouble(); 
34                     Trapezoid tra = new Trapezoid(a,b,h);
35                     Card card4 = new Card(tra);
36                     list.add(card4);
37 //                 System.out.println("1");
38                    break;
39                 } 
40             }
41         }
42     }

  還有我在寫排序的時候,把排序方式升按照升序排序,而不是降序排序寫。因為我弄反了比較數和被比較數的順序(粗心)。

  7(7-2):這道題沒掉什麼坑,因為是按7(7-1)改的,坑都在7(7-1)掉完了。就是在寫輸出格式的時候略有點麻煩而已。

  8(7-1):我在寫這道題的時候,在初始化的時候遇到了難題。就是在初始化的時候一直處理不好資料儲存這一塊。因為我是用ArrayList儲存的。但在呼叫ArrayList裡面的資料的時候,常常會出現null的情況,尤其是在其他類裡面呼叫,報錯提示null的情況就更多了。從而導致我之後的計算無法進行(會報錯null)。還有我做的從下到上的用於查詢的迭代器也沒寫好,不能準確找出相應的資料(即無法用卡號找賬戶),因而也不能修改賬戶餘額。

  9(7-1):這道題是用老師給的參考程式碼8(7-1)改的。一開始我沒弄好多型(即把所有資料都存入Account抽象類裡面),把資料直接分別存在相對應型別的賬戶裡,導致不能通過呼叫抽象Account類來呼叫,等寫到最後還要寫好幾個僅僅換了一個類名的方法,而且還導致最後實現取款功能的時候還要根據賬戶型別寫不同的取款方法(即使是實現相同的功能,但就是要寫好幾個)。還有Card類,我之前也把它分為了兩種,然後程式碼寫了一堆還是不能執行。最後重新理了一遍,才明白只有賬戶需要用多型,才從坑裡面爬出來。

修改前                            修改後

(4)改進建議:

  7(7-1):感覺真沒什麼可以改的了。

  7(7-2):在面積排序那一塊相同面積排序可以試試比較器的三目運算子的寫法,可以有效減少程式碼量。因為都是同類圖形,所以不用考慮圖形名稱的問題。

1 card.sort((area1,area2)->{return area1.getArea()>area2.getArea()?1:area1.getArea()<area2.getArea()?-1:0;});

  8(7-1):資料初始化要改。我設計的初始化呼叫只要出了初始化所在的方法(注意:是方法!甚至不是類),就會有幾個資料出現null的情況。然後為避免這個問題,我每次使用資料都要new一個新的初始化物件來呼叫資料,很麻煩。而且用迭代器來找資料能找到,但不能找到它的上一級是什麼(無法實現從下往上找)。這是“單向連結串列”的弊端。所以要把單向連結串列改為雙向連結串列。

  9(7-1):賬戶多型的那一塊要做修改。我對於賬戶貸款餘額並沒有在賬戶類裡面設定一個屬性,而是作為一個數值放在取款裡面。可嘗試將貸款餘額作為一個私有屬性加到不同賬戶類裡面去。這樣就不用把餘額最低值設定為負數了,設計更為合理。

(5)總結:

  通過這三次題目集,我學到了開閉原則的理解,使用多型實現開閉原則,使用迭代器查詢資料,雙向連結串列的實際使用,更加深刻理解了類的繼承。通過圖形卡片的題目,我更深刻理解了ArrayList的使用方法,和類的繼承,抽象方法的完善。通過ATM機類結構設計的題目,我學會了雙向連結串列的使用,和資料儲存的新方式。不過我在使用迭代器查詢資料方面還不熟練,hasNext(),obj.iterator()等方法似懂非懂;多型的使用還是時常會弄糊塗,寫抽象類的時候總是忘了只要寫它子類裡面的相同方法,導致在有些子類裡面用不到的方法也要重寫(不寫就報錯),看來還是要多學學。其他方面就沒什麼了。