OOP第三次总结Blog
1. 前言
相比于前一次Blog题目集,此次七八九题目集偏重于类的继承、多态性使用方法以及接口的应用;在设计层面,强调模式复用,不断的迭代,若前期设计不合理,则后续的题目增加新的功能(即可扩展性)将会变得更加困难,三次题目集共四道题,两道一迭代.
第七次题目集一共两道题<图形卡片>,通过这次的题目集,可以进一步深入了解面向对象设计原则中的"单一职责"以及"开-闭"原则,同时,也运用到了Comparable接口,是一个非常方便的排序封装接口.极大的减少了对象排序的工作量,耗时2h
第八次题目集为ATM机设计,其中涉及到的实体与实体间的关系相比于前一题目集提升了不少,除了考察基本的编程思维以外,还考察对类信息获取的敏感度,能否迅速的捋清楚所需要的类和其中的关系,是解这题的关键,耗时6h
第九次题目集为ATM机设计的迭代版,相比于第八次题目集,新增银行账户类型,跨行办理业务、手续费的收取规则等接近现实生活规则的业务,整体的结构没有发生太大的变化,除了在原有的初始化数据上添加新数据之外,还需要添加几个业务处理函数,难度体现在此,在计算费用的时候踩了不少坑,导致部分数据计算一直有误差.耗时4h.
2.设计与分析
题目集7-1 图形卡片排序游戏
类图如下
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 图形卡片分组游戏
类图如下:
本次作业主要对卡片(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机类结构设计(一)
类图如下:
主要考察的是类的封装性及类间关系设计(关联、组合及依赖),在设计类与类之间的关系时候,为了减少其中的耦合性,我采用了设计数据库的思想,实体类的创建独立存在,互不影响,专门创建一些关系类,比如说AccountConnectCard类就是用Map存的账户与银行卡号的业务关系类(一个账户可以有多个银行卡号,但是一个银行卡号只能对应一个账户,那么这种特殊的对应方式,就可以把账户作为value,银行卡号作为key,这样在查询的时候,可以通过get(key)方法,直接获取对应的账户,极大的减少了查询的时间,ATMConBankName同理,ATM号与银行为多对一),那么Map的映射方式比较适合于两个属性之间的一对一,大于两个属性关系的存储关系,就需要用到对象数组ArrayList了,在UserInfoKindList中采用的的即是如此
在InitUserInfo中采用的初始化信息存储
虽然类和类之间并没有直接的关系,但是通过这些类似于数据库中的表,将他们之间的关系间接的建立起来(虽然这里没有数据库完整性,但是设计的思想确实也同时简化了设计程序的复杂性!!!)。这些类的建立优势体现在业务处理的时候,也就是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机类结构设计(二)
类图如下:
相对于题目集8,由于要处理的业务有添加,同时对原有的业务逻辑也有了变更,所以在原有的基础上添加了些许一对一类属性表,账户-类型表,账户类型-账户类型代号表,跨行业务办理汇率表.
在变更的过程中,取钱的业务主体业务逻辑框架无太大变化,不过如何处理金额的变化成为了设计的一大难题.在上一个题目集中,由于对跨行业务办理无相应处理,所以逻辑非常的简洁,那么加入了跨行规则之后,就需要对整体的逻辑进行一个重构:在同样获取当前账户的对象之后,需要先获取当前的余额,判断其是否为跨行办理业务.若是,则获取目标银行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之后,不仅没有阻止,反而鼓励透支(规则设置位置考虑不够全面),这样开下去的银行,不过多久就要倒闭了
在没有发现这个问题之前,获取的测试结果是这样的:
事实证明,再这样透支下去,花呗都救不了我自己(=w=)
解决方法,在目前的钱设置完之后,再进行一次判断,这个比喻就相当于在进考场之前设置一道关卡,出考场的最后一刻再设置一道检验!这样就可以确保最后的透支不会超过50000了
进行改进后,再次测试(成功遏制超前消费50000!)
- 对跨行取钱透支的算法理解出错,只收了跨行取款的钱,对超出透支的余额,未做另外的增额,这么一算的话,会经常便宜一点钱,如果钱的基数一大...(好吧,不敢想象了,跟金钱有关的东西还是要非常严谨的!在做之前最好先跟着脑中的思路笔算一下,确认无误后再进行编程设计,因为这也就是解决数学问题嘛,不动笔的话很容易算错的);
在发现问题的时候,测试结果是这样的:
但是题目说明书给出的测试用例结果确是:
通过图片的对比可以发现,在跨行超额取款的时候,汇率出现了问题,于是针对这个节点,我开始了试错之旅,在想这多出来的56块钱到底是哪里来的,不可能是ATM他自个吐出来的吧,之后,对这个数字的来源进行了多方向的测试,发现原来需要进行如下的操作:
1.若为信用卡,计算出透支余额,并存储
2.退出判断后,对透支余额进行升值,中国银联制定的5%
3.将增值后的透支余额加上目前处理金额,得到最后的取款金额
改进后,测试结果如下:
银行可以不用亏钱了yep!!
4.改进建议
其实除了那些一定要存在数组里面的多类属性对应关系以外,一对一类关系存在Map这个数据结构中真的非常方便,同样的,通过类的关系设计图,刚开始想到的是树的数据结构,自顶向下,由中国银联父节点一直延伸到各个层次开枝散叶,不过在设计的过程中也发现了些许错误,最后还是决定将所有的类看成同一级别的设计,当然,每一个数据结构的适用场景不同,在存储数据前,不妨思考一下不同数据结构设计会给自己的程序设计带来什么样的结果,在对比中不断优化,没有最好的设计,只有不断优化的过程。其次,如果类间的关系不会是一层扣一层的嵌套很深的话,采用一个实体类包含另外实体类的对象设计也是可以的。
5.总结
提升的主要方面:类的继承、多态性使用方法以及接口的应用、类的封装性、类间关系设计,完善了类设计的思想,越来越偏向于高复用性的代码设计,也会在其中不断优化自己的设计思路,使得其愈发简洁清晰.
其次,养成一个良好的编程习惯真的很重要,分情况判断处理不要堆叠在一起,每一种情况记得写好注释(没有注释的代码,过一段时间你自己都看不明白自己写了什么,这样重构起来大部分情况下是很痛苦的=w=),在书写方法之前,一定要将处理方法的流程过一遍,如果出现什么情况,处理的办法是什么,需要怎么实现,如果在走过程的时候发现无法解决的问题或者解决起来非常复杂的问题,这个时候就需要反过来思考是不是前面的结构设计有问题?或者思考解决的思路出现了问题,书写之前记得判断方法是否可行,这样才不会在没有必要的地方浪费大部分时间.
OOP第三次总结Blog的更多相关文章
- oop 第三次作业 文件读写
oop第三次作业 GitHub 对于迟交我感到很抱歉 031602510 体会 这次的用到了之前的文件读写,传参 定义函数有返回值,使代码更加简洁.面向对象的程序设计,在面对函数多元的情况下,编程更加 ...
- 深入浅出OOP(三): 多态和继承(动态绑定/运行时多态)
在前面的文章中,我们介绍了编译期多态.params关键字.实例化.base关键字等.本节我们来关注另外一种多态:运行时多态, 运行时多态也叫迟绑定. 运行时多态或迟绑定.动态绑定 在C#语音中,运行时 ...
- Java OOP——第三章 多态
1.多态:(polymorphism): 是具有表现多种形态能力的特征: (专业化的说法:)同一个实现接口(引用类型),使用不同的实例而执行不同的操作 指一个引用(类型)在不同情况下的多种状态.也可以 ...
- oop的三种设计模式(单例、工厂、策略)
参考网站 单例模式: 废话不多说,我们直接上代码: <?php /** 三私一公 *私有的静态属性:保存类的单例 *私有的__construct():阻止在类的外部实例化 *私有的__clone ...
- OOP第三章博客
OO第三单元博客 • (1)梳理JML语言的理论基础.应用工具链情况: 理论基础: 网络资料上面介绍JML有两种主要的用法: 开展规格化设计.这样交给代码实现人员的将不是可能带有内在模糊性.二义性的自 ...
- JavaScript OOP(三):prototype原型对象(即构造函数的prototype属性)
通过构造函数生成的实例化对象,无法共享属性或方法(即每个实例化对象上都有构造函数中的属性和方法):造成了一定的资源浪费 function Obj(name,age){ this.name=name; ...
- OOP第三次上机
上机问题 T1 CSet 还是熟悉的CSet,只是多了个构造函数以及收缩空间. T2 SingleTon 单例问题. 用一个指针保存唯一的实例,用户无法在外部直接新建实例,只能使用外部接口(函数),函 ...
- 面向对象(OOP)三
一.面向对象基础原则 1)单一职责原则(类要写得小而精,低耦合) 内部类 单列模式 对于单一职责原则,其核心思想为:一个类,最好只做一件事,只有一个引起它的变化.单一职责原则可以看做是低耦合.高内聚在 ...
- OOP复习笔记
/*OOP相关的代名词不做讲解*/ OOP的三大特征: 封装 - 继承 - 多态 -----------------------------------目录---------------------- ...
随机推荐
- Day004 顺序结构
顺序结构 JAVA的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行. 顺序结构是最简单的算法结构. 语句与语句之间,是按照从上到下的顺序进行的,它是由若干个依次执行的处理步骤组成的,它 ...
- Sublime text3 的破解
下载sublimeText3的安装包并安装(已经安装的可以忽略) 在hosts文件中添加:127.0.0.1 license.sublimehq.com(hosts文件地址:C:\Windows\Sy ...
- app.json文件配置
pages 页面路径的存放列表 通过手写页面路径可以在对应目录下自动创建对应文件 "pages": [ "pages/demo3/demo3", "p ...
- [技术博客]iview组件样式踩坑记录
[技术博客]iview组件样式踩坑记录 iview官方文档. 在本次项目开发中,前端项目主要使用vue框架+iview组件构建,其中iview组件在使用过程中遇到了许多官方文档中没有明确说明或是很难注 ...
- 微服务·API文档
阅文时长 | 3.92分钟 字数统计 | 2754.05字符 主要内容 | 1.什么是API文档 2.API文档的使用 3.声明与参考资料 『微服务·API文档』 编写人 | SCscHero 编写时 ...
- jQ的隐式迭代和设置样式属性
jQ中的隐式迭代 意义:不需要原生迭代了,在jQ内部自动帮你实现了循环 代码实现: let arr = document.querySelectorAll('li') for(let i = 0;i ...
- [bug] Flask:jinja2.exceptions.UndefinedError: 'None' has no attribute 'id'
问题 Python Flask做的购物网站,添加购物车时,提示错误 解决 检查发现是MySQL中不正常的空数据导致,删除此条记录即可 参考 https://www.jb51.cc/python/186 ...
- QT windows 应用程序 exe ,设置详细信息并解决中文乱码问题
原博主:https://blog.csdn.net/xiezhongyuan07/article/details/87691490 1.新创建一个.rc文件,随意命名,例如叫app.rc 并编辑 1 ...
- 《SystemVerilog验证-测试平台编写指南》学习 - 第2章 数据类型
<SystemVerilog验证-测试平台编写指南>学习 - 第2章 数据类型 2.1 内建数据类型 2.2 定宽数组 2.2.1 声明 2.2.2 常量数组 2.2.3 基本的数组操作 ...
- SSH 远程控制
本文以 Ubuntu 20.04(客户端) 控制 Kali Linux 2020.2(服务端)为例 1.安装SSH(secure Shell) SSH分为客户端oppenssh-client和服务端o ...