java题目集第三阶段总结

java题目集第三阶段总结

1前言

题目集7主要涉及类的继承、多态性使用方法以及接口的应用。题目7-1要求实现对不同形状的图形按面积进行排序,7-2则多要求按图形种类进行分类排序。总体难度比较传统,只要吃透类图基本不会有太大困难。

java题目集第三阶段总结

题目集8只有一道题,atm机模拟程序,主要涉及综合性的软件设计知识,要写出优雅的代码难度相对比较高,对设计思想也是一个考验。对于类的组织要经过深思熟虑,按照符合直觉的方法规划类的层次,同时善用业务类进行解耦。

 java题目集第三阶段总结

 

 

题目集9是题目集8的优化,新增了对于信用卡透支和跨行取款的支持,这增加了取款程序的复杂度,也增加了类的种类和继承层次,尤其是取款的时候要具体的账户类型,而银行管理的是抽象的账户,类型判断给程序添加了更多复杂性。

 java题目集第三阶段总结

 

 

(2)设计与分析不同

  题目集7 7-1

 

 java题目集第三阶段总结

 

 

处理这道题先让所有图形类从shape继承,shape有类型名的属性和返回面积的方法,可以实现不同图形了之间的比较,和类形名的输出同时兼具了判断图形合法性的方法,这些方法都由子类实现,但外部的类又基于shape的抽象调用。Card类中包含一个shape类的实例,并且实现了Comparable<>接口,DealCardList类型维护了一个Card类的链表,并且利用list类自带的sort方法实现了card对象的排序。输入的检查,和DealCardList的维护则是在main中完成。

 java题目集第三阶段总结

 

 

可见,程序的分支是比较少的,代码复杂度也比较低。

题目集7 7-2

7-2的类图与7-1基本一致,DealCardList中添加了四种toString,分别从DealCardList维护的Card链表中利用getShgapeName选择出对应的子类输出,分类排序输出利用了同样的方法。当一个链表是有序的,那么其中任何一部分都是有序的。

 java题目集第三阶段总结

 

 

这两道题目运用了类的继承和接口两种抽象方式,大大减少了代码的复杂度,简化了提高了代码的可读性与可维护可拓展性。对外部隐藏了复杂性让程序的各个部分解耦合。Card类,dealcardlist都是基于shape的抽象进行编程。

题目集8 7-1

  这道题的难点是如何组织如此多的实体类,并且确定方法是由什么类实现,以及在保证实现高效的情况下保证系统的解耦。

 java题目集第三阶段总结

 

 

我的实现是Card包含一个Account引用,指向Card关联的账户,具有password属性和number(卡号)属性,而Account则包含一个Card链表,一个User对象的引用代表账户的所有者,一个Bank对象代表账户所属的银行,number属性则代表账户号,余额也有Account存储。

ChinaUnionPay对象则维护这一个bank链表,并且实现了查找账户,卡号,银行的方法。

BankServer类则包含一个ChinaUnionPay对象,和一个User链表,并且实现了查找账户,卡号,银行,atm,存取款,检查取款合法性的方法。以及维护user链表和账户,银行卡,银行,atm的方法。

BankServer中的Check方法用来检查取款合法性,先利用卡号和atm名找到具体的Card对象和ATM对象,如找不到则返回相应错误,查找的方式是自上而下,深度优先的查找。然后会利用Card对象获相应的Account对象比较余额,之后会用同样的方法比较Bank,一切无误后返回true。

取款方法则相似,先检查取款合法性,再查找Card对象,再利用Card获取Account对项,修改余额值完成取款并输出结果。

 

从结果而言,效果不错。但实际上存在这样那样的问题。

首先是查找方法过于耦合,复杂度高。要调用BankServe查找Card对象,首先要调用每个Bank的查找方法,Bank再调用每个Account的查找方法,查找方法耦合过于严重,而且因为对象在其“上级”中都是用List储存,查找效率低。

此外是业务类设计不合理,业务类本来应该是抽象的服务,但BankServe却实际上储存了银联和用户,不符合直觉。而且BankServe这个类过于宽泛了,实际上业务类应该专门处理某一类业务,而不是什么业务都处理。

 java题目集第三阶段总结

 

 

改进方法:为Card和Account类实现compareable<>接口,并用set优化储存,Bank和银联同时管理一个Card的Set对象,银联管理一个Account的Set对象,优化查找和减少耦合,银联管理一个用户set对象。
BankServe不再储存银联和用户列表,而是在运行时传入卡号等信息。把BankServe拆分,拆分为检查合法性的类和存取款的类。

题目集9 7-1

这道题相对于题目集8增加了对跨行取款,和信用卡透支的支持,对软件编码和设计提出了更高的要求。

 java题目集第三阶段总结

 

 

这个题目中我为Account对象添加了两个子类,一个是信用账户,一个是借记账户,Card有两个子类,信用卡和借记卡,分别对应两种账户。业务类分两个,分别用来检查合法性,和存取款。

取款方法比较复杂,先检查卡,atm是否存在,在检查密码是否正确,然后先计算贷款利息(为了防止取款导致余额变化无法确定透支部分),然后判断是否跨行,如跨行就计算手续费,二者费用相加。然后利用insteadof判断Account的类型,并基于类型判断取款是否合法,取款并输出结果。

整个取款方法相比题目集8过程更加复杂,而且要保证计算过程中余额不变,否则可能导致费用计算错误,所以取款过程先后是经过仔细推敲确定的。

在这个题目集中,卡的类型和账户的类型在定义时是确定的,而在实际管理中是抽象的,但是在取款过程中由需要具体的类型,于是我设计了两个insteadof的条件判断语句来确认账户是哪个实际类型。

虽然将对类的操作抽象出来作为业务类是非常实用的解耦方法,但是针对抽象的业务类,难以直接处理各种具体的子类。事实上如果在每个具体类中添加工具方法,或许能减少类型判断从而是方法更加优雅,程序也更加具有可维护性。

 java题目集第三阶段总结

 

 

采坑心得

在实际编码过程中,尤其是atm的两道题目,我一开始始终希望把所有方法都放在相应的实体类当中,所以话大量的时间去思考检查取款合法性是应该属于atm类,还是银行类,还是账户类,取款动作的发出是由用户,atm,还是银行。一旦踏入了这种误区,我的思想就在里面绕来绕去,想不到方案,要不然就是太耦合,每个类都要相互配合操作,要不然就是不够直观,不符合直觉,总之一直没什么好办法。最后我联想到正则表达式相关的几个类,才尝试业务类的方案,虽然做了出来但还是认为不够优雅。最终看到答案代码才觉得,业务类是比较通用的做法。

在写形状的两道题目的时候,我对于使用接口还是非常陌生的,因为网络上资料良莠不齐,而且时间跨度很大,对比起来就非常乱。而且对于直接我不熟悉的接口和类总是有一种不信任,没把握的感觉,所以主客观的困难让我有点艰难。好在是过程比较平缓,很容易就做出来了,还学到了lamda表达式。

改进建议

在我自己的实现中基本上是很少使用接口来抽象的,实际上很多地方用接口是可以让代码更加简洁优雅的,比如题目集九,可以为Account的子类提供取款接口,再在业务类中调用相应的接口方法,就可以完全避免在类中判断类型导致耦合和可扩展性下降,此外在题目集7中使用接口来判断具体类型,而不是显示的定义String类型的字段是更好的解决方法。

此外,在题目集九中一部分判断结果输出实际上是位于主函数里面的,这种图方便的做法是非常不好的,实际上应该内置到业务类里面,保证功能的完整统一,减少不必要的复杂度。

总结

不得不说,对着类图编程是一种很奇怪的经历,在编程过程中,我要尝试和类图“达成共识”,要理解类图为什么这么设计,实际上是要干什么。如果对类图产生误读,或者不理解,那么对算法的实现又会有很大的影响。所以实际体验有时候又像是负重前行,忍受一些“不好”的设计。但在完全自己设计类的继承结构和算法之前,这种学习方式就像是一种规范,即不让我们倒退回过程的编程,也不让我们在类的设计上野蛮生长,这种引导的效果还是非常不错的。

除了设计意外,对于父类和抽象类的设计也让我受益颇多,利用抽象类,和父类或者接口,结合java自带的数据结构方法模板,能够快速方便的实现复杂的功能,并且减少bug的出现,合理的利用list,set,和增强的for循环,能大大提高效率,是非常值得推荐的做法。

在数据存储的位置方面,同一个数据可以同时存在不同的实体中,尽管这些增强的程序的耦合,但是能大大降低算法的复杂度,从而提高效率。而这种方法提供的自然而然的同步,也是非常有用的,相对于一级一级向下查找,这种方法实际上可以说利大于弊。

上一篇:【设计模式自习室】桥接模式 Bridge Pattern:处理多维度变化


下一篇:对Java题目集7-9的总结性Blog