Java的第三次博客作业
一.前言
1.知识点:ArrayList泛型的应用方法、Comparable接口及泛型的应用、继承,多态的应用、面向对象八大原则的运用、类的封装性及类间关系设计(关联、组合及依赖)、抽象类的使用、迭代器的使用;
2.题量:题量很少,题目集7就2两道题,题目集8和题目集9都是一道题;
3.难度:题目集7很简单,主要就是实现Comparable接口这个,上CSDN上搜一下很容易就可以知道怎么实现;题目集8较难,对于各个类的设计需要巧妙的思想,数据的初始化也是一个比较繁琐的任务,对于怎么找到根据输入卡号找到对应的账号并进行使用这一点比较难实现(迭代器可以很好的实现,但是自己并没有想到怎么用);题目集9比较简单,老师给了源码,确实非常妙,尤其是根据卡号找到对应的账户那段代码很好,根据老师的源码稍加修改就很容易做出题目了。
二.设计与分析
提示:以下所以类图和SourceMonitor的生成报表中的test类均为测试类。
①题目集7(7-1)、(7-2)两道题目的递进式设计分析总结:
这两道题都是对于图形的类型、参数、面积等进行相关的操作,第一道题的排序,我了解到ArrayList类里面有一个方法是Collections.sort(list),他可以将ArrayList里面的元素按照一定的规则进行排序,而这个规则就是由元素里面实现的Comparable接口来决定的,于是我进行对Comparable接口的实现:重写方法compareTo(T),具体代码如下:
1 @Override 2 public int compareTo(Card card) { 3 // TODO Auto-generated method stub 4 if(this.shape.getArea() > card.shape.getArea()) { 5 return -1; 6 }else if(this.shape.getArea() < card.shape.getArea()) { 7 return 1; 8 } 9 return 0; 10 }
以下是7-1的类图:

由类图可以得知:类Circle、类Rectangle、类Triangle及类Trapezoid均继承于抽象类Shape,类Shape中有属性shapeName、校验数据方法validate()、求面积方法getArea()、构造方法和属性的get,set方法,根据子类的不同会重写validate()、getArea()和toString(),子类是各个图形所以也会有各自相应的属性,如三角形的三边,圆的半径等;类Card实现Comparable接口,有属性Shape(一张卡片对应一个图形),而重写的compareTo也是为了能够根据卡片对卡片进行排序;类DealCardList则是业务类,方法validate()返回一个boolean型值用于判断输入数据是否正确,方法cardSort()则是用于对卡片进行排序,getAllArea()是返回所有面积之和,showResult()是展示输出结果;
以下是7-2的类图:
复杂度图如下:

第二道题与第一道题区别并不大,只是在类DealCardList上有些许差别,实际上这个类写的并不好,其原因我认为是第一题中类Shape做成了抽象类导致无法实现父类指向子类这种多态形式,所以第二题中类DealCardList我对每个图形都分开操作了,这也导致复杂度多了很多,也写了很多垃圾代码,如;
1 ArrayList<Card> cardList = new ArrayList<Card>(); 2 ArrayList<Card> cardList1 = new ArrayList<Card>(); 3 ArrayList<Card> cardList2 = new ArrayList<Card>(); 4 ArrayList<Card> cardList3 = new ArrayList<Card>(); 5 ArrayList<Card> cardList4 = new ArrayList<Card>();
如果用了多态只需要写第一行的一行代码即可。
踩坑:对于继承抽象类和多态的选择不好,会导致代码比较垃圾。
②题目集8和题目集9两道ATM机仿真题目的设计思路分析总结
题目集8:这道题看指导书很重要,老师带着我们看了一遍指导书(这很关键),然后根据指导书的要求一步步写代码,但实际上还是会有许多困难的,比如属性金额是放在账户类Account还是银行卡类Card中(其实是我上课没认真听讲)、根据卡号找到账户并对其进行操作、数据的初始化和更新数据等。这道题首先创建银联,并进行初始化数据(方法f()是初始化),然后再创建一个业务类Function的对象进行输入的一行数据的操作,业务类Function中有一个getStr()的方法是根据空格来分割字符串返回一个字符串数组,代码如下:
1 public String[] getStr() { 2 return str.split("\\s+"); 3 }
方法f1()是存款或者取款(这点不好,应该分开写的),方法f2()是功能查询余额,方法isExistCardNum()是判断卡号是否存在,方法isExistATMNum()是判断ATM机是否存在,方法validate()是判断银行卡密码是否正确,方法Print()是输出,方法isEnjambment() 是判断是否跨行(这点用了迭代器)代码如下:
1 public boolean isEnjambment() { //判断是否跨行 2 String ATMNO = getStr()[2]; 3 4 Iterator<ATM> ATMItr = bank.getAtms().iterator(); 5 while(ATMItr.hasNext()) { 6 if(ATMItr.next().getNumber().equals(ATMNO)) { 7 return true; 8 } 9 } 10 11 return false; 12 }
类图如下

由类图可以看到类ChinaUnionPay、类Bank、类User、类Account、类Card、类ATM都是实体类,类Function是业务类,在类与类之间可以看到有ArrayList<T>这种由属性聚合的关系,也有
1 public void addAtms(ATM atms) { 2 this.atms.add(atms); 3 } 4 5 public void removeAtms(ATM atms) { 6 this.atms.remove(atms); 7 }
这种代码,我觉得这种处理很适合未知元素数量的多少,也可以随时加随时减,总的来说类与类之间的关系设计是挺糟糕的,自己有时候也会搞混来。
踩坑:类与类之间的关系并没有考虑清楚;指导书看的不够仔细;上课没认真听讲,忽略掉了关键的信息;实际上我写代码的时候写了挺多业务类的但是没用一次业务类都要重新提取数据并无法保持实时更新,最后把所有业务写在了一起,这导致很多命名不规范,代码也写的问题多多;初始化数据的时候写的很艰难,因为我是用的下标了一个一个list.add()这样加,这种方法很 不好,应该用循环好一些;迭代器的使用也没用好,没有用到迭代器真正的用处,我仅仅是用了最浅显的遍历作用,所以我写了挺多for语句的(一开始写的switch-case),代码如下:
1 for(int i = 0;i < a.getList().size();i++) { 2 for(int j = 0;j < a.getList().get(i).getUsers().size();j++) { 3 for(int k = 0;k < a.getList().get(i).getUsers().get(j).getAccount().size();k++) { 4 for(int m1 = 0;m1 < a.getList().get(i).getUsers().get(j).getAccount().get(k).getCards().size();m1++) { 5 if(a.getList().get(i).getUsers().get(j).getAccount().get(k). 6 getCards().get(m1).getCardNum().equals(CardNum)) { 7 bank = a.getList().get(i); 8 if(isEnjambment() == false) { 9 System.out.println("Sorry,cross-bank withdrawal is not supported."); 10 System.exit(0); 11 } 12 a.getList().get(i).getUsers().get(j).getAccount().get(k).Withdraw(s); 13 name1 = a.getList().get(i).getUsers().get(j).getName(); 14 name2 = a.getList().get(i).getName(); 15 balance = a.getList().get(i).getUsers().get(j).getAccount().get(k).getBalance(); 16 System.out.println(name1 + "在" + name2 + "的" + getStr()[2] + "号ATM机上" + st + "¥" + getStr()[3].replace("-", "")); 17 System.out.println("当前余额为¥" + balance + ".00"); 18 } 19 } 20 } 21 } 22 }
题目集9:这道题老师给了题目集8的源码,只需要进行修改和加几个功能就可以完成题目要求,重点是看老师给的源码,老师的源码确实有很多很妙的写法是我所没想到的,当然最关键的还是类与类之间的设计。首先是将老师给的源码的Withdraw里面的校验等分开写成一个类。题目集9的类图如下:

对于信用卡和借记卡的处理我是选择写CreditAccount和DebitAccount,继承自类Account,然后在类Account中添加属性type(用于判断账户类型),而CreditAccount和DebitAccount两个类的不同也仅是type属性的不同以及CreditAccount中多了一个属性overdraft(透支额度),所以主要做了修改的代码就是取款那一部分了,代码如下:
1 public void withdraw() { 2 3 Account account = Account.getAccountbyCardNO(cardNO); 4 double balance = account.getBalance(); 5 ATM aTM = ValidateData.getATMbyATMID(unionPay, ATMID); 6 Card card = ValidateData.getCardbyCardNO(unionPay, cardNO); 7 8 9 //取款更新余额操作 10 if (account.getBank().getBankNO() != aTM.getBank().getBankNO()) { 11 double balance1 = account.getBalance(); 12 account.setBalance(balance - amount - amount*aTM.getBank().getRatio()); 13 14 if(account.getType().equals("aa") && (amount > balance)) { 15 account.setBalance(account.getBalance() - getInterest(balance1,amount)); 16 17 } 18 }else { 19 double balance1 = account.getBalance(); 20 account.setBalance(balance - amount); 21 22 if(account.getType().equals("aa") && (amount > balance)) { 23 account.setBalance(account.getBalance() - getInterest(balance1,amount)); 24 } 25 } 26 27 if(amount >= 0) { 28 showResult(account,1); 29 }else { 30 showResult(account,0); 31 } 32 33 }
对于题目集9我觉得增加几个功能并不是主要学习的,更加重要的是学习老师类与类之间的设计(前提要学会看懂代码),所以我讲一下部分在我看来老师源码比较优秀的部分
1.双向绑定。例如类Bank和类Account都互相有对方的属性,这一点在初始化中体验的淋漓尽致可以将信息紧密的绑定在一起;
2.迭代器的使用。源码中用写一个方法返回一个对象,使用迭代器来获取对象,代码如下:
1 public static Card getCardbyCardNO(UnionPay unionPay,String cardNO) { 2 Card card = null; 3 Iterator<Bank> bankItr = unionPay.getBankList().iterator(); 4 5 while(bankItr.hasNext()) { 6 ArrayList<Account> accountList = bankItr.next().getAccountList(); 7 Iterator<Account> accountItr = accountList.iterator(); 8 while(accountItr.hasNext()) { 9 ArrayList<Card> cardList = accountItr.next().getList(); 10 Iterator<Card> cardItr = cardList.iterator(); 11 while(cardItr.hasNext()) { 12 card = cardItr.next(); 13 if(card.getCardNO().equals(cardNO)) { 14 return card; 15 } 16 } 17 } 18 } 19 return null; 20 }
可以看到第6、9、12行中都用了ITr.next()的这种方法,确实很巧妙,不过在我写题目集8的时候我也看到了这种写法,但是却没想到可以另外写一个方法来返回对象,后续也是可以根据返回值是否为null来判断数据是否存在;
踩坑:在算透支利息和跨行手续费的时候,忽略了一些东西如当balance<amount<balance+overdraft的时候,是要先算跨行手续费然后在算透支利息(用透支的金额算)
总之算钱这个东西确实是挺难的,两种额外费用如果不细心思考很容易踩坑,还有一个很关键的!!!题目的人民币符号“¥”和键盘上打出来的不一样“¥”,我也不知道为啥,之后尽量复制题目中的符号吧。
三.总结
1.类与类之间的设计真的真的非常重要,这种能力我也不知道怎么具体提示,我也只能多看优秀的代码,自己多动手写一下较复杂的工程类题吧;
2.题目集7学到一种写法感觉很棒,代码如下:
1 //在Main类中定义一个静态Scanner对象,这样在其它类中如果想要使用该对象进行输入,则直接 2 //使用Main.input.next…即可(避免采坑) 3 public static Scanner input = new Scanner(System.in);
3.虽然感觉自己这ATM机的代码写的很垃圾,但是也能明显感觉到自己的工程思想变好了很多,思考问题也更加全面了,对于类的设计无疑是更好了 ,但是还不够,在之后的学习中我会更加注重类与类之间的联系,加强我类设计的能力;
4.题目量虽少,但是我觉得比之前那些题目更有训练价值吧,这三次题目集中能感觉到我已经逐渐懂了一点点面向对象的思想了。

浙公网安备 33010602011771号