OOP第三次總結Blog

  • 2021 年 6 月 20 日
  • 筆記

1. 前言

相比於前一次Blog題目集,此次七八九題目集偏重於類的繼承、多態性使用方法以及接口的應用;在設計層面,強調模式復用,不斷的迭代,若前期設計不合理,則後續的題目增加新的功能(即可擴展性)將會變得更加困難,三次題目集共四道題,兩道一迭代.

  • 第七次題目集一共兩道題<圖形卡片>,通過這次的題目集,可以進一步深入了解面向對象設計原則中的”單一職責”以及”開-閉”原則,同時,也運用到了Comparable接口,是一個非常方便的排序封裝接口.極大的減少了對象排序的工作量,耗時2h
    image

  • 第八次題目集為ATM機設計,其中涉及到的實體與實體間的關係相比於前一題目集提升了不少,除了考察基本的編程思維以外,還考察對類信息獲取的敏感度,能否迅速的捋清楚所需要的類和其中的關係,是解這題的關鍵,耗時6h
    image

  • 第九次題目集為ATM機設計的迭代版,相比於第八次題目集,新增銀行賬戶類型,跨行辦理業務、手續費的收取規則等接近現實生活規則的業務,整體的結構沒有發生太大的變化,除了在原有的初始化數據上添加新數據之外,還需要添加幾個業務處理函數,難度體現在此,在計算費用的時候踩了不少坑,導致部分數據計算一直有誤差.耗時4h.
    image


2.設計與分析

題目集7-1 圖形卡片排序遊戲

類圖如下

image

Rectangle,Circle,Trapezoid,Triangle類均繼承於一個共同的父類Shape,因為需要同時處理許多這四類中的對象,所以在業務類DealCardList中創建了Shape類的對象數組,將需要處理的信息封裝,那麼在處理的時候只需要判斷輸入的多少,決定創建什麼類型的對象即可,實例代碼如下:

while (iterator.hasNext()){
        choice = (int)iterator.next();

        switch (choice){
            case 1:
                radius = Main.input.nextDouble();
                cardList.add(new Card(new Circle(radius)));
                break;
            case 2:
                width = Main.input.nextDouble();
                length = Main.input.nextDouble();
                cardList.add(new Card(new Rectangle(width,length)));
                break;
            case 3:
                side1 = Main.input.nextDouble();
                side2 = Main.input.nextDouble();
                side3 = Main.input.nextDouble();
                cardList.add(new Card(new Triangle(side1,side2,side3)));
                break;
            case 4:
                topSide = Main.input.nextDouble();
                bottomSide = Main.input.nextDouble();
                height = Main.input.nextDouble();
                cardList.add(new Card(new Trapezoid(topSide,bottomSide,height)));
                break;
        }`

SourceMonitor分析結果

Complexity, Statements, Max Depth, Calls
Card.Card() 1, 0, 0, 0
Card.Card() 1, 1, 2, 0
Card.compareTo() 2, 4, 3, 3
Card.getShape() 1, 1, 2, 0
Card.setShape() 1, 1, 2, 0
Circle.Circle() 1, 0, 0, 0
Circle.Circle() 1, 2, 2, 1
Circle.getArea() 1, 1, 2, 0
Circle.getRadius() 1, 1, 2, 0
Circle.setRadius() 1, 1, 2, 0
Circle.validate() 1, 1, 2, 0
DealCardList.DealCardList() 1, 0, 0, 0
DealCardList.DealCardList() 7, 27, 5, 15
Main.main() 5, 14, 4, 11
Rectangle.getArea() 1, 1, 2, 0
Rectangle.getLength() 1, 1, 2, 0
Rectangle.getWidth() 1, 1, 2, 0
Rectangle.Rectangle() 1, 0, 0, 0
Rectangle.Rectangle() 1, 3, 2, 1
Rectangle.setLength() 1, 1, 2, 0
Rectangle.setWidth() 1, 1, 2, 0
Rectangle.validate() 2, 1, 2, 0
Trapezoid.getArea() 1, 1, 2, 0
Trapezoid.Trapezoid() 1, 0, 0, 0
Trapezoid.Trapezoid() 1, 4, 2, 1
Trapezoid.validate() 3, 1, 2, 0
Triangle.getArea() 1, 2, 2, 2
Triangle.Triangle() 1, 0, 0, 0
Triangle.Triangle() 1, 4, 2, 1

這裡DealCardList即為處理業務邏輯的主方法


題目集7-2 圖形卡片分組遊戲

類圖如下:

image

本次作業主要對卡片(Card)進行分組遊戲,其規則為隨機發放一些卡片給學生,卡片仍然分為四種形狀:圓形(Circle)、矩形(Rectangle)、三角形(Triangle)及梯形(Trapezoid),並給出各種卡片的相應參數,要求學生首先根據卡片類型將所有卡片進行分組(一個類型分為一組,所以最多四組),然後能夠對每組內的卡片根據面積值從大到小進行排序,同時求出該組內所有卡片的面積之和,最後求出每組卡片面積之和中的最大值。

這次的作業多了一個業務,叫做圖片分組,需要對獲取的Shape類對象數組分成四組,並根據不同的計算規則處理數據,所以在區分它們的時候,想到的辦法就是遍歷判斷,都是Shape大類的實例,但它們也有屬於自己的類,所以就自然而然的聯想到instanceof關鍵字,用來判斷其為哪個類的實例化,主要的業務處理類為DealMutiCardList類,其中核心相關分類代碼如下:

public void SeparatedArray(){
    ArrayList<Card> cardList = dealCardList.getCardList();
    Iterator<Card> cardIterator = cardList.iterator();
    Card temp;
    while (cardIterator.hasNext()){
        temp = cardIterator.next();
        if(temp.getShape() instanceof Circle){
            circles.add(temp);
        }else if(temp.getShape() instanceof Rectangle){
            rectangles.add(temp);
        }else if(temp.getShape() instanceof Triangle){
            triangles.add(temp);
        }else if(temp.getShape() instanceof Trapezoid){
            trapezoids.add(temp);
        }
    }
}

SourceMonitor分析結果

Complexity, Statements, Max Depth, Calls
Card.Card() 1, 0, 0, 0
Card.Card() 1, 1, 2, 0
Card.compareTo() 2, 4, 3, 3
Card.getShape() 1, 1, 2, 0
Card.setShape() 1, 1, 2, 0
Circle.Circle() 1, 0, 0, 0
Circle.Circle() 1, 2, 2, 1
Circle.getArea() 1, 1, 2, 0
Circle.getRadius() 1, 1, 2, 0
Circle.setRadius() 1, 1, 2, 0
Circle.validate() 1, 1, 2, 0
DealCardList.DealCardList() 8, 30, 5, 17
DealCardList.getCardList() 1, 1, 2, 0
DealMutiCardList.DealMutiCardList() 1, 1, 2, 0
Main.main() 5, 16, 4, 12
Rectangle.getArea() 1, 1, 2, 0
Rectangle.getLength() 1, 1, 2, 0
Rectangle.getWidth() 1, 1, 2, 0
Rectangle.Rectangle() 1, 0, 0, 0
Rectangle.Rectangle() 1, 3, 2, 1
Rectangle.setLength() 1, 1, 2, 0
Rectangle.setWidth() 1, 1, 2, 0
Rectangle.validate() 2, 1, 2, 0
Trapezoid.getArea() 1, 1, 2, 0
Trapezoid.Trapezoid() 1, 0, 0, 0
Trapezoid.Trapezoid() 1, 4, 2, 1
Trapezoid.validate() 3, 1, 2, 0
Triangle.getArea() 1, 2, 2, 2
Triangle.Triangle() 1, 0, 0, 0
Triangle.Triangle() 1, 4, 2, 1
Triangle.validate() 4, 1, 2, 0


題目集8 ATM機類結構設計(一)

類圖如下:

image

主要考察的是類的封裝性及類間關係設計(關聯、組合及依賴),在設計類與類之間的關係時候,為了減少其中的耦合性,我採用了設計數據庫的思想,實體類的創建獨立存在,互不影響,專門創建一些關係類,比如說AccountConnectCard類就是用Map存的賬戶與銀行卡號的業務關係類(一個賬戶可以有多個銀行卡號,但是一個銀行卡號只能對應一個賬戶,那麼這種特殊的對應方式,就可以把賬戶作為value,銀行卡號作為key,這樣在查詢的時候,可以通過get(key)方法,直接獲取對應的賬戶,極大的減少了查詢的時間,ATMConBankName同理,ATM號與銀行為多對一),那麼Map的映射方式比較適合於兩個屬性之間的一對一,大於兩個屬性關係的存儲關係,就需要用到對象數組ArrayList了,在UserInfoKindList中採用的的即是如此
image
在InitUserInfo中採用的初始化信息存儲
image

雖然類和類之間並沒有直接的關係,但是通過這些類似於數據庫中的表,將他們之間的關係間接的建立起來(雖然這裡沒有數據庫完整性,但是設計的思想確實也同時簡化了設計程序的複雜性!!!)。這些類的建立優勢體現在業務處理的時候,也就是HandleBusiness類,在其中創建了關係類的實體對象,在處理相應業務的時候,只需要像查表一樣,使用其get方法,極大的簡化了代碼量,示例如下

public boolean atmInfoExist(String atmNum){
    for (String keynum :
            atmConBankName.getAtmConBankName().keySet()) {
       if(keynum.equals(atmNum)){
           return true;
       }
   }
   return false;
   <-上面為原來的寫法,大部分都是遍歷查詢,當數據量大的時候,查詢花費的時間對比差就會越大,下方為優化寫法,運行結果相同->
    HashMap<String, String> atmConBankName = this.atmConBankName.getAtmConBankName();
    return (atmConBankName.get(atmNum)!=null);

}

當然這樣寫其實也有一個相對來說不太完美的地方,就是如果數據類型一多,關係也同樣跟着複雜了,如果大部分都用這個來設計的話,其實需要建的表是非常多的,有時候需要去查的可能就那麼幾個,同時在設計之初,也比較難捋清楚所有的關係,因為其中沒有各種約束,就會產生關係之間對應不上的情況,這個問題在添加初始化數據的時候尤為明顯,稍微一出差錯..(別問我怎麼知道的,說多了都是淚哇QAQ)

其中主要的業務邏輯處理方法為DealAllInfo方法
代碼如下:

	//1.判斷卡號是否存在
    if(!cardNumExist(cardNum)){
        System.out.println("Sorry,this card does not exist.");
        System.exit(0);
    }
    //2.判斷輸入的ATM機編號是否存在
    if (!atmInfoExist(ATMNum)){
        System.out.println("Sorry,the ATM's id is wrong.");
        System.exit(0);
    }
    //3.判斷輸入的賬號密碼是否匹配
    if(!cardInfoMatch(cardNum,passWord)){
        System.out.println("Sorry,your password is wrong.");
        System.exit(0);
    }
    4.跨行存取款
    if(!atmMatchBank(cardNum,ATMNum)){
        System.out.println("Sorry,cross-bank withdrawal is not supported.");
        System.exit(0);
    }

    5.開啟存取款業務
    if(money<0){
        //輸入數據小於0 存錢
        saveMoney(cardNum,ATMNum,money);
    }else if(money>0){
        //輸入數據大於0 取錢
        withdrawMoney(cardNum, ATMNum, money);
    }

存錢的實現業務流程如下:
通過accountConnectCard表中的銀行卡號獲取賬戶號,再憑藉此賬戶號去UserInfoKindList類中的初始化表找到該條信息在ArrayList中對應的下標,最後憑藉獲取的下標,對其設置賬戶剩餘金錢;

     //獲取相應的賬戶號
	String accountNum = accountConnectCard.getCardConAccount().get(cardNum); //獲取賬戶號
    //只需要獲取相應的賬戶賬號所在的序號index
    int index = 0;
    ArrayList<UserInfoKindList> oriUser = initUserInfo.getOriUser();
    //遍歷獲取index
    for (int i = 0; i < oriUser.size(); i++) {
        if(accountNum.equals(oriUser.get(i).getAccount().getAccountNumber())){
            index = i;
            break;
        }
    }
    UserInfoKindList tempRec = initUserInfo.getOriUser().get(index);
    //設置金錢
    initUserInfo.getOriUser().get(index).getAccount().setBalance(initUserInfo.getOriUser().get(index).getAccount().getBalance()-money);
    //現存餘額
    double NowBalance = initUserInfo.getOriUser().get(index).getAccount().getBalance();
    System.out.print(tempRec.getUser().getName()+
            "在"+atmConBankName.getAtmConBankName().get(ATMNum)+"的"+ATMNum+"號ATM機上存款¥");
    System.out.printf("%.2f\n",(-money));
    System.out.printf("當前餘額為¥%.2f\n",NowBalance);

取錢的業務如下:
通過accountConnectCard表中的銀行卡號獲取賬戶號,再憑藉此賬戶號去UserInfoKindList類中的初始化表找到該條信息在ArrayList中對應的下標,最後憑藉獲取的下標,對其設置賬戶剩餘金錢,其實這裡的業務流程與存錢的幾乎是一樣的,唯一需要注意的就是在取錢之前,需要對輸入的取錢金額做一個校驗,若你需要取的錢多於卡里有的錢,那麼就會被認定為非法取錢,否則,就進行取錢業務,對相應賬戶進行扣除餘額,相應代碼如下

if(OriBalance < money){
        System.out.println("Sorry,your account balance is insufficient.");
        System.exit(0);
    }else{
        oriUser.get(index).getAccount().setBalance(OriBalance-money);
        System.out.print(tempRec.getUser().getName()+
                "在"+atmConBankName.getAtmConBankName().get(ATMNum)+"的"+ATMNum+"號ATM機上取款¥");
        System.out.printf("%.2f\n",money);
        NowBalance =  oriUser.get(index).getAccount().getBalance();
        System.out.printf("當前餘額為¥%.2f\n",NowBalance);
    }

SourceMonitor分析結果

Complexity, Statements, Max Depth, Calls
Account.Account() 1, 0, 0, 0
Account.Account() 1, 2, 2, 0
Account.getAccountNumber() 1, 1, 2, 0
Account.getBalance() 1, 1, 2, 0
Account.setAccountNumber() 1, 1, 2, 0
Account.setBalance() 1, 1, 2, 0
AccountConnectCard.AccountConnectCard() 1, 1, 2, 1
AccountConnectCard.AccountConnectCard() 1, 1, 2, 0
AccountConnectCard.getCardConAccount() 1, 1, 2, 0
AccountConnectCard.init() 1, 10, 2, 10
AccountConnectCard.setCardConAccount() 1, 1, 2, 0
ATM.ATM() 1, 0, 0, 0
ATM.ATM() 1, 1, 2, 0
ATM.getATMNum() 1, 1, 2, 0
ATM.setATMNum() 1, 1, 2, 0
ATMConBankName.ATMConBankName() 1, 1, 2, 1
ATMConBankName.ATMConBankName() 1, 1, 2, 0
ATMConBankName.getAtmConBankName() 1, 1, 2, 0
ATMConBankName.init() 1, 6, 2, 6
ATMConBankName.setAtmConBankName() 1, 1, 2, 0
atmMatchBank().cardInfoMatch() 4, 7, 4, 7
atmMatchBank().cardNumExist() 3, 7, 4, 5
atmMatchBank().saveMoney() 3, 12, 4, 12
atmMatchBank().withdrawMoney() 5, 17, 4, 14
Bank.Bank() 1, 0, 0, 0
Bank.Bank() 1, 1, 2, 0
Bank.getBankName() 1, 1, 2, 0
Bank.setBankName() 1, 1, 2, 0
Card.Card() 1, 0, 0, 0
Card.Card() 1, 1, 2, 0
Card.Card() 1, 2, 2, 0
Card.getCardNum() 1, 1, 2, 0
Card.getPassWord() 1, 1, 2, 0
Card.setCardNum() 1, 1, 2, 0
Card.setPassWord() 1, 1, 2, 0
ChinaUnionPay.ChinaUnionPay() 1, 1, 2, 0
ChinaUnionPay.getUnionName() 1, 1, 2, 0
ChinaUnionPay.setUnionName() 1, 1, 2, 0
HandleBusiness.DealCardNum() 4, 10, 4, 11
HandleBusiness.HandleBusiness() 1, 0, 0, 0
InitCard.getCardArrayList() 1, 1, 2, 0
InitCard.InitCard() 1, 1, 2, 1
InitCard.InitCard() 1, 1, 2, 0
InitCard.initCardNum() 1, 10, 2, 10
InitCard.setCardArrayList() 1, 1, 2, 0
InitUserInfo.InitUserInfo() 1, 1, 2, 1
InitUserInfo.InitUserInfo() 1, 1, 2, 0
Main.main() 6, 23, 4, 13

圈複雜度也不高,因為沒有嵌套判斷查詢.

題目集9 ATM機類結構設計(二)

類圖如下:

image

相對於題目集8,由於要處理的業務有添加,同時對原有的業務邏輯也有了變更,所以在原有的基礎上添加了些許一對一類屬性表,賬戶-類型表,賬戶類型-賬戶類型代號表,跨行業務辦理匯率表.

image

在變更的過程中,取錢的業務主體業務邏輯框架無太大變化,不過如何處理金額的變化成為了設計的一大難題.在上一個題目集中,由於對跨行業務辦理無相應處理,所以邏輯非常的簡潔,那麼加入了跨行規則之後,就需要對整體的邏輯進行一個重構:在同樣獲取當前賬戶的對象之後,需要先獲取當前的餘額,判斷其是否為跨行辦理業務.若是,則獲取目標銀行ATM對應費率,獲取之後對錢進行疊加,若不是,則不對其進行處理;處理完金錢增值後,需要判斷的是透支層,在判斷前,需要對當前的卡號進行是否為信用卡判斷,若為信用卡,則讓其透支,並記錄下透支超過了多少錢(後續需要對透支超過的錢進行變換匯率增值!!!這裡是我踩過的數學計算坑=w=),若不是信用卡,檢測到透支後,直接退出並輸出Sorry,your account balance is insufficient.;

最後再對錢進行設置.完成取錢業務(其餘部分設計與上一題類似,這裡就不做贅述)

函數代碼如下:

   //取錢
public void withdrawMoney(String cardNum,String ATMNum,double money){
    //獲取相應的賬戶號
    String accountNum = accountConnectCard.getCardConAccount().get(cardNum); //獲取賬戶號
    //只需要獲取相應的賬戶賬號所在的序號index
    int index = 0;
    ArrayList<UserInfoKindList> oriUser = initUserInfo.getOriUser();
    //遍歷獲取index
    for (int i = 0; i < oriUser.size(); i++) {
        if(accountNum.equals(oriUser.get(i).getAccount().getAccountNumber())){
            index = i;
            break;
        }
    }

    //在設置之前,需要對錢做一些手腳
    HashMap<String, String> atmConBKN = this.atmConBankName.getAtmConBankName();
    HashMap<String, Double> bknConRates = crossBankRate.getBKNConRate(); //跨行利率
    double NowMoney = money; //目前需要進行扣取的錢
    double Rates = 1.0; //當前錢率

    //獲取當前的餘額
    double OriBalance = oriUser.get(index).getAccount().getBalance();
    double NowBalance = 0; //取錢之後的餘額
    UserInfoKindList tempRec = oriUser.get(index);
    double LoanMoney = 0; //超出的金額

    //1.判斷是否跨行辦理
    if(!atmMatchBank(cardNum,ATMNum)){
        //獲取相應ATM對應銀行的費率
        String BKN = atmConBKN.get(ATMNum); //獲取銀行名稱
        Rates = Rates + bknConRates.get(BKN);//通過銀行名獲取利率
    }

    //對前面的Money進行疊加 NowMoney是要取出來的錢
    NowMoney = NowMoney * Rates;

    //取錢這塊做一個判斷,是否為信用卡
    //要取的錢是否大於現存的錢
    if(OriBalance < NowMoney){
        //不是信用卡,就不讓透支
        if(!isLoanAccount(cardNum)){
            System.out.println("Sorry,your account balance is insufficient.");
            System.exit(0);
        }else{
            //是信用卡,才讓透支
            //這裡判斷超出了多少錢
            if(OriBalance>=0){
                LoanMoney = money - OriBalance;
            }else{
                LoanMoney = money;
            }
        }
    }

    //若為信用卡,透支多×0.05
    NowMoney += (LoanMoney * 0.05);

    //對用戶的錢進行設置
    oriUser.get(index).getAccount().setBalance(OriBalance-NowMoney);
    NowBalance =  oriUser.get(index).getAccount().getBalance();
    //設置完之後進行判斷,若為信用卡,且餘額小於-50000,即超出透支額度,退出;
    if ((NowBalance < -50000)){
        System.out.println("Sorry,your account balance is insufficient.");
        System.exit(0);
    }

    System.out.print("業務:取款 "+tempRec.getUser().getName()+
            "在"+atmConBankName.getAtmConBankName().get(ATMNum)+"的"+ATMNum+"號ATM機上取款¥");
    System.out.printf("%.2f\n",money);

    System.out.printf("當前餘額為¥%.2f\n",NowBalance);
}

}


SourceMonitor分析結果

Complexity, Statements, Max Depth, Calls
Account.Account() 1, 0, 0, 0
Account.Account() 1, 2, 2, 0
Account.getAccountNumber() 1, 1, 2, 0
Account.getBalance() 1, 1, 2, 0
Account.setAccountNumber() 1, 1, 2, 0
Account.setBalance() 1, 1, 2, 0
AccountConnectCard.AccountConnectCard() 1, 1, 2, 1
AccountConnectCard.AccountConnectCard() 1, 1, 2, 0
AccountConnectCard.getCardConAccount() 1, 1, 2, 0
AccountConnectCard.init() 1, 15, 2, 15
AccountConnectCard.setCardConAccount() 1, 1, 2, 0
AccountConType.AccountConType() 1, 1, 2, 1
AccountConType.AccountConType() 1, 1, 2, 0
AccountConType.getAccConTypeNum() 1, 1, 2, 0
AccountConType.init() 1, 12, 2, 12
AccountConType.setAccConTypeNum() 1, 1, 2, 0
AccountType.AccountType() 1, 1, 2, 1
AccountType.AccountType() 1, 1, 2, 0
AccountType.getNumConAccount() 1, 1, 2, 0
AccountType.setNumConAccount() 1, 1, 2, 0
ATM.ATM() 1, 0, 0, 0
ATM.ATM() 1, 1, 2, 0
ATM.getATMNum() 1, 1, 2, 0
ATM.setATMNum() 1, 1, 2, 0
ATMConBankName.ATMConBankName() 1, 1, 2, 1
ATMConBankName.ATMConBankName() 1, 1, 2, 0
ATMConBankName.getAtmConBankName() 1, 1, 2, 0
ATMConBankName.init() 1, 11, 2, 11
ATMConBankName.setAtmConBankName() 1, 1, 2, 0
atmMatchBank().cardInfoMatch() 4, 7, 4, 7
atmMatchBank().cardNumExist() 3, 7, 4, 5
atmMatchBank().saveMoney() 3, 12, 4, 12
atmMatchBank().withdrawMoney() 10, 29, 6, 18
Bank.Bank() 1, 0, 0, 0
Bank.Bank() 1, 1, 2, 0
Bank.getBankName() 1, 1, 2, 0
Bank.setBankName() 1, 1, 2, 0
Card.Card() 1, 0, 0, 0
Card.Card() 1, 1, 2, 0
Card.Card() 1, 2, 2, 0
Card.getCardNum() 1, 1, 2, 0
Card.getPassWord() 1, 1, 2, 0
Card.setCardNum() 1, 1, 2, 0
Card.setPassWord() 1, 1, 2, 0
ChinaUnionPay.ChinaUnionPay() 1, 1, 2, 0
ChinaUnionPay.getUnionName() 1, 1, 2, 0
ChinaUnionPay.setUnionName() 1, 1, 2, 0
CrossBankRate.CrossBankRate() 1, 1, 2, 1
CrossBankRate.CrossBankRate() 1, 1, 2, 0
CrossBankRate.getBKNConRate() 1, 1, 2, 0
CrossBankRate.init() 1, 3, 2, 3
CrossBankRate.setBKNConRate() 1, 1, 2, 0
HandleBusiness.DealCardNum() 4, 10, 4, 11
HandleBusiness.HandleBusiness() 1, 0, 0, 0
InitCard.getCardArrayList() 1, 1, 3, 0
InitCard.InitCard() 1, 1, 3, 1
InitCard.InitCard() 1, 1, 3, 0
InitCard.initCardNum() 1, 15, 3, 15
InitCard.setCardArrayList() 1, 1, 3, 0
InitUserInfo.getOriUser() 1, 1, 3, 0
InitUserInfo.init() 1, 7, 3, 7
InitUserInfo.InitUserInfo() 1, 1, 3, 1
InitUserInfo.InitUserInfo() 1, 1, 3, 0
InitUserInfo.setOriUser() 1, 1, 3, 0
Main.main() 6, 22, 4, 12
User.getName() 1, 1, 3, 0
User.setName() 1, 1, 3, 0
User.User() 1, 0, 0, 0
User.User() 1, 1, 3, 0
UserInfoKindList.getAccount() 1, 1, 3, 0
UserInfoKindList.getBank() 1, 1, 3, 0
UserInfoKindList.getUser() 1, 1, 3, 0
UserInfoKindList.setAccount() 1, 1, 3, 0
UserInfoKindList.setBank() 1, 1, 3, 0
UserInfoKindList.setUser() 1, 1, 3, 0
UserInfoKindList.UserInfoKindList() 1, 0, 0, 0
UserInfoKindList.UserInfoKindList() 1, 3, 3, 3

3.采坑心得

在取錢的過程中,不僅設計糊塗,我感覺到我人也糊糊塗塗的~
具體表現在

  • 透支50000之後,不僅沒有阻止,反而鼓勵透支(規則設置位置考慮不夠全面),這樣開下去的銀行,不過多久就要倒閉了

在沒有發現這個問題之前,獲取的測試結果是這樣的:
image

事實證明,再這樣透支下去,花唄都救不了我自己(=w=)

解決方法,在目前的錢設置完之後,再進行一次判斷,這個比喻就相當於在進考場之前設置一道關卡,出考場的最後一刻再設置一道檢驗!這樣就可以確保最後的透支不會超過50000了

image

進行改進後,再次測試(成功遏制超前消費50000!)

image


  • 對跨行取錢透支的算法理解出錯,只收了跨行取款的錢,對超出透支的餘額,未做另外的增額,這麼一算的話,會經常便宜一點錢,如果錢的基數一大…(好吧,不敢想像了,跟金錢有關的東西還是要非常嚴謹的!在做之前最好先跟着腦中的思路筆算一下,確認無誤後再進行編程設計,因為這也就是解決數學問題嘛,不動筆的話很容易算錯的);

在發現問題的時候,測試結果是這樣的:

image

但是題目說明書給出的測試用例結果確是:

image

通過圖片的對比可以發現,在跨行超額取款的時候,匯率出現了問題,於是針對這個節點,我開始了試錯之旅,在想這多出來的56塊錢到底是哪裡來的,不可能是ATM他自個吐出來的吧,之後,對這個數字的來源進行了多方向的測試,發現原來需要進行如下的操作:
1.若為信用卡,計算出透支餘額,並存儲
2.退出判斷後,對透支餘額進行升值,中國銀聯制定的5%
3.將增值後的透支餘額加上目前處理金額,得到最後的取款金額

image

改進後,測試結果如下:

image

銀行可以不用虧錢了yep!!


4.改進建議

其實除了那些一定要存在數組裏面的多類屬性對應關係以外,一對一類關係存在Map這個數據結構中真的非常方便,同樣的,通過類的關係設計圖,剛開始想到的是樹的數據結構,自頂向下,由中國銀聯父節點一直延伸到各個層次開枝散葉,不過在設計的過程中也發現了些許錯誤,最後還是決定將所有的類看成同一級別的設計,當然,每一個數據結構的適用場景不同,在存儲數據前,不妨思考一下不同數據結構設計會給自己的程序設計帶來什麼樣的結果,在對比中不斷優化,沒有最好的設計,只有不斷優化的過程。其次,如果類間的關係不會是一層扣一層的嵌套很深的話,採用一個實體類包含另外實體類的對象設計也是可以的。


5.總結

  1. 提升的主要方面:類的繼承、多態性使用方法以及接口的應用、類的封裝性、類間關係設計,完善了類設計的思想,越來越偏向於高復用性的代碼設計,也會在其中不斷優化自己的設計思路,使得其愈發簡潔清晰.

  2. 其次,養成一個良好的編程習慣真的很重要,分情況判斷處理不要堆疊在一起,每一種情況記得寫好注釋(沒有注釋的代碼,過一段時間你自己都看不明白自己寫了什麼,這樣重構起來大部分情況下是很痛苦的=w=),在書寫方法之前,一定要將處理方法的流程過一遍,如果出現什麼情況,處理的辦法是什麼,需要怎麼實現,如果在走過程的時候發現無法解決的問題或者解決起來非常複雜的問題,這個時候就需要反過來思考是不是前面的結構設計有問題?或者思考解決的思路出現了問題,書寫之前記得判斷方法是否可行,這樣才不會在沒有必要的地方浪費大部分時間.