1. 前言:
题目集分析:
题目集4的题量为七道题,其中的第二小题和第三小题比较相似,都是对一组数据经行去重操作,考察的知识点和array有关;第四题考察的是String类相关的知识,包括其中用到的split和indexOf等方法;第五题考察的知识点是类相关的知识,主要是将数据域设为private达到封装效果;第六题考察对数据经行处理;第七题题目与题目集三中的题目相似,但是它是判断两个日期的先后、判断日期间的天数差以及判断周数,这一道题目允许使用Date类的一些方法,因此题目便变得十分简单了,因为核心算法都能够由方法来实现,不像题目集三需要自己编写,难点就在于需要自己去学习与LocalDate类有关的知识;第一题是这个题目集的难度担当,光是看着这个长长的题目,就令人肃然起敬,望而生畏。
题目集5的题量为六道题,第一道题考察的知识点是正则表达式,难度较小,判断QQ号是否符合规范;第二题考察String和array的相关知识,对字符串经行排序,使用arrry中的sort方法可以快速解决问题;第三题和第四题也是对正则表达式的考察,难度较小;第五题和第六是熟悉的日期求前n天、后n天以及天数差的问题,可以说是题目集5里的难度之最了,不过核心算法类似于题目集三,关键在于把它改成不同的类比如Year、Month、Day等,难点在于厘清类与类之间的关系。
题目集6的题量为一道题,虽然是三个题目集中题量最小的,但是它的难度完全超过了其他两个题目集。光是题目就是洋洋洒洒几百字,它考察了类的设计等知识点。我能感受到的最大难点就是,这一次的题目不同于之前考察类的题目,它要求你通过自己的理解来定义不同的类、设计类之间的关系。之前的题目都是明确给出了几个类、什么关系、什么结构,而这道题除了基础的几个类已经给出,其他的类需要自己来构思,这是一个很大的挑战。最难的点在于如何使各个类联系起来,其实解决菜的价格等问题的算法并不复杂,复杂的是类与类之间该如何去构思、如何去协调作用。还有如何添加类中的属性,才能覆盖到所有的可能出现的异常情况。
2. 设计与分析:
题目集5的7-5:
这题需要设计DateUtil、Year、Month、Day四个类,在PowerDesigner中的相应类图如下
首先定义Year类,除了有参与无参构造方法和getter、setter方法外,还包含了isLeapYear方法来判断是否为闰年,以及validdate方法来判断年份是否有效,Year与Month、Day这三个类中都由进一和减一的方法。再定义Month类,Month类聚合了Year类,它的有参构造方法可以传入Month、Year的值。除了getter、setter方法,Month还有restMin、restMax方法,分别将月份重置为1月份和12月份。再定义Day类,Day类聚合了Month类,其数据域中还包括了mon_maxnum数组,用来表示不同月份的天数。有参构造方法可以传入Day、Month、Year的值,其他的方法如getter、setter、restMin、restMax等如Month、Year中相似。再定义DateUtil,DateUtil类聚合了Day。DateUtil类是实现的各种功能的核心类,它包含了checkInputValidity方法,用来判断输入的有效性;还有compareDates方法,用来比较两个类之间的大小;getNextNdays方法,用来得到下N天;getPreviousDays方法,用来得到前N天;getDaysOfDates,得到两个日期的天数差。
类中的具体算法与题目集3中相似,不同之处在于类之间不能直接引用,而要通过getter来实现。Main方法中设计一个整数choice,通过输入的值来判断实现哪一种功能。通过if-else语句来实现不同的数值对应不同的功能。再不同的功能中创建DateUtil类,每次输入Day、Month、 Year的值时首先利用判断checkInputValidity方法来判断输入的值是否有效,再用针对不同的数值使用getNextNDays或PreviousNDays方法。
在SourceMonitor的生成报表内容显示平均复杂度(Average Complexity)为4.26。
题目集5的7-6:
这一题与上一题相似,分别设计了DateUtil、Year、Month、Day四个类,但是,不同于上一题,这一题中,Day类不聚合Month类,Month类不聚合Year类,而是由DateUtil类直接聚合聚合Year、Month、Day这三个类,在PowerDesigner中的生成的类图如下
这一题与上一题高度相似,首先定义Year类,包含有参和无参构造方法、getter、setter和判断闰年、判断输入是否有效的方法,与上一题一致。再定义Month类,除了没有聚合Year类,其他的方法等与上一题一致,比如restMin、restMax等方法。Day类没有聚合Month类,其他的与上一题一致。再定义DateUtil类,它聚合了Day类、Month类、Year类实现求下N天,前N天的,天数差的核心算法不发生改变,比如 getNextNDays、getPreviousNDays等方法,需要改变的是当引用各个变量时,只需用getter函数即可。与上一题相比类之间的结构变得更加简单,再上一题中,引用year的值,需要Day.getMonth().getYear().getValue(),而这里直接调用Year.getValue()即可。
在SourceMonitor的生成报表内容显示平均复杂度(Average Complexity)为1.74。
这里的平均复杂度比上一题低,因为修改了getNextNDays和getPreviousNDays方法的核心算法,使用了循环来实现,复杂度直线下降。
题目集6的7-1:
这道题需要设计Dish类、Menu类、Record类、Order类、Table类、Tables类、DateCompute类,在PowerDesigner中的生成的类图如下
首先定义Dish类,它的数据域包含了菜名name、基础价格unit_price、菜品种species(是否为特色菜)、格式参数isWrong。由于有是否为特色菜的区别,因此有不同的构造方法,由于要判断格式是否正确,还有带isWrong的构造方法。除了getter、setter方法外,Dish类还有isSpecial方法来判断该菜品是否为特色菜。
再定义Menu类,它聚合了Dish类,使用ArrayList来建立Dish类数组。使用Arraylist能简化增加和删除菜品的过程。它包含了searchDish方法,通过输入菜名用来判断该菜品是否存在于菜单之中;addDish方法,通过输入菜名、基础价格来添加一道菜品至菜单中; addDishWithWrong方法,与addDish相似,但多传入了错误标记isWrong,便于再菜品输入结束后输出错误的菜品;deleteDish方法,通过输入菜名来删除一道菜单中的菜品;checkThefrom方法和checkThePrice方法,分别用来判断菜品的格式和价格是否符合规范。
再定义Record类,Record类的数据域包含点菜序号orderNum、菜品d、份额portion、份数num、参数hasDelete、vality、isWrong分别用来判断它是否被删除、格式有效性、数据有效性。 除了构造方法和getter、setter方法外,它还包含了getPrice方法,根据份额、份数用来计算这一订单的价格;getPriceWithSale方法,需要传入参数sale,根据折扣来判断打折后的价格;checkRecordByName方法,通过传入菜单,判断该订单中的菜品是否存在于菜单之中;checkFollow、checkThePortion、checkTheNum三个方法分别用来判断订单中的桌号顺序、份额大小、份数多少是否符合规范。
再定义Order类,Order类中包含聚合了Record类,用ArrayList类来形成Record类数组,数据域中还包含了numOfcha用来判断混合在订单信息中的菜品信息。除了构造方法和getter、setter方法外,Order类还包含了reviesTheFollow方法,用来剔除错序输入的菜单,修正订单顺序;getTotalPrice方法,用来判断订单的总价;getTotalPriceWithSale方法,通过输入折扣来判断订单大众后的总价;add和delete方法用来对订单进行增删的操作;各种serach、find、get方法通过输入不同的信息比如菜品名、序号等来判断、得到订单信息;
ShowOrds方法来展示订单的处理过程;showDelete方法用来展示已经删除的订单的详细处理过程。
定义DateCompute类,数据域中包含year、month、day、hours、minus、second分别表示创建订单的年月日时秒分。Mon_maxnum数组表示不同月份的天数最大值。除了构造方法外,DateComputer还包含checkDate方法,判断日期的格式;isLeapYear方法,判断年份是否为闰年;checkForm方法,判段日期的格式(题目中要求年份四位数,其他的一或两位数);checkInputVality方法,用来判断输入的日期是否有效;checkPeriod方法,用来判段订单创建时期是否在【2022.1.1-2023.12.31】有效时间段内;dateToweek方法,将日期转换为星期几,从而协助Sale方法得到当前时间段的折扣;checkOpenning方法,用来判断输入的日期是否在营业的时间内;Sale方法,根据创建订单的时间得到当前的折扣数,协助getPriceWihSale等方法完成价格计算。
定义Table类,它聚合了Order类和DateCompute类。其数据域中包含了桌号num、订单order、年月日date、时分秒date2、时间处理datecomputer、参数isWrong判断有效性。除了getter、setter方法和构造方法外,Table类还包含了showStatus方法,用来输出该桌的所有处理信息,包括混淆的错误菜品、删除菜品的具体处理过程、订单处理过程;checkNum方法,用来判断桌号是否符合格式。
定义Tables类,Tables类聚合了Table类,用ArrayList来实现Table数组除了构造方法和getter、setter方法,Tables类还定义了addATable方法,用来添加一个桌子;checkTakebleByNumOfOrder方法,通过桌号来判断桌子是否存在;reviseTheOrdesWithTakeWorng方法,将不符合格式的桌子的桌号修改为符合格式的桌子的桌号,便于判断桌号顺序问题;showStatusWithTimeCheck方法输出每一桌的价格;showProcessOfOrders方法输出每一桌的具体处理过程;showOfProcessOfDelete方法输出每一桌的顶单中的删除过程。
Main方法中创建了Menu、Order、Tables等类,并通过多个正则表达式来判断输入的信息是菜品信息还是订单信息,在调用各个中的add方法和showStatus方法,完成输出,在检查到end时,结束循环,停止程序。
在SourceMonitor的生成报表内容显示平均复杂度(Average Complexity)为4.24。
3. 踩坑心得:
题目集4中的第二题逻辑错误导致输出错误的结果

原因是循环的设计条件出现问题,应该将
改为
第二题最大的难点是当样例的数据很大的时候,往往简单的方法是行不通的
这样会无法通过第二个第三个和第四个测试点,最后的解决方法是删去二分查找法 直接将元素与邻位比较,这样就能通过测试点。
题目集5中的在表示不同月份的天数时,用到的mon_maxnum数组只定义到12,即表示12月,在程序运行时会出现问题,数组越界导致错误
解决方法时将数组多定义几个月,即定义13月份的值,但是不会使用,能够防止数组越界。
如果在判断闰年后将mon_maxnum数组中的2月份改为29天,就会导致错误
部分的测试点无法通过,解决方法是在每个更新年份都进行一次判断,重新对mon_maxnum进行赋值。
在部分测试点会出现非零返回的错误

解决方法是将月份数组的值扩大 防止在输入越界月份时还未终止输出Wrong Format就数组越界
还有一个坑就是代码长度超过限制,尤其是你在代码写的过于复杂的时候,就会 出现这个问题
在题目集6中出现了getOrder函数返回为空的问题,数据域变得多,类结构变得复杂,往往就会有一些地方容易出现疏忽。
解决方法是在只有三个参数的构造函数中将order进行赋值。
在题目集6中需要时刻注意数组越界问题,有时需要添加菜品,有时有需要添加订单,有时需要添加桌子,有时需要删除订单,一不留神就会出现数组越界的错误。因为数组下标要随着输入的信息时刻更新,下标需要不断变化,不然就会出现各种各样的越界错误。


Dish类中的查找出现问题,导致serchDish方法输出错误的结果,问题是使用了 == 来比较俩字符串之间是否相等,比较字符串时必须使用equal方法来比较,不然就会得到不正确的比较结果,导致方法出现逻辑错误。
4. 改进建议:
题目集5中的7-5中的复杂度4.26,原因是在getNextNDays方法和getPreviousNDays方法写的太过复杂,可以将其中对n不断取余的操作简化,利用循环,设置条件为n>=0,每次循环执行完之后,都将n减去一,直至循环结束,这样复杂度就会减少为1.74,并且不会出现代码长度超过限制的错误。

还有需要改进的地方就是题目集6中的对于带点菜功能的完善,题目要求订单创立的时候除了本桌的订单内容,还能够实现对其他桌子的代点菜功能,我的编写过程中并未实现这个功能,需要在Table类再添加一个属性,是代点菜的总价格,首先需要对代点菜的桌号进行判断,判断其是否存在,然后再计算它的价格,并且并入到Table的总价中去。
还有题目集6中的程序需要改进一下

在类图中可以看出,许多类中都有属性为isWrong、validity等,这是一开始没有考虑到题目的一些细节的要求,在后续的编写中强加进去,只是为了实现某个特定的判断功能,这样的编写复用性太差了,完全不符合编写程序的基本原则,应当将这些属性抽象出来,改进程序,增强复用性。



应当将这些isWrong属性、validity属性抽象出来,组成一个方法。
5. 总结:
通过这三次题目集的练习,我学会了很多实用的java知识。 比如说在使用正则表达式过程中,我发现正则表达式在面对多种复杂的情况时确实时很好用的工具,比如在区分输入的信息是菜品信息还是订单信息,正则表达式利用很简短的语句就能够区分。

再比如在程序中使用ArrayList。ArrayList在实现增加与删除元素的时候很简洁便利,使用add和remove方法即可,还能判断是否存在相同的元素,非常好用。还有LinkedHashSet也是非常实用的java知识,在一组数据中去掉重复的数据,HashSet能够快速的实现,并且不会破环输入时的顺序。

通过题目集6,我才深刻领会到类图规划到底有多重要。我在编写的过程中,没有事先对程序进行详细的规划和需求分析,导致在后续的编写过程中不断的去修改之前已编写的代码,只是为了实现一些之前没有注意到的需求与功能,并且添加了类似于isWrong、validity等属性,这些都是在编程到一半的时候才发现自己的代码框架无法覆盖到全部的需求,不得不添加一些额外的属性来判断,从而实现某些现有框架未覆盖到的功能。因此写代码前一定要类图规划分析,不然就会得不偿失,大大的增加代码量。











