oop作业总结2

yf3455 / 2023-04-27 / 原文

一.前言

       这是oop第二阶段pta大总结,学习了继承与多态,并且也算完整的体验了一两个小的程序,从日期的最终版,再到菜单计价小程序,让我们自己也学习了很多的东西,第四次的pta作业我用到了Linkhashset,treeset等的使用,以及通过查询Java API文档,进行了解Scanner类中nextLine()方法、String类中split()方法、Integer类中parseInt()方法的用法,了解LocalDate类中of()、isAfter()、isBefore()、until()等方法的使用规则,了解ChronoUnit类中DAYS、WEEKS、MONTHS等单位的用法。对越来越多的关于Java的小的知识有了自己的体会。第五次的pta作业进行了正则表达式的训练,对验证码的校验,还有学号的验证具有一定的了解,初试利器,然后进行了日期迭代,采用了不一样的设计原则,进行比较。第六次pta作业只有一道题,难度没有很大,但是多种情况的细分让人很头疼,不知道测试点的具体样例。

二.分析与设计

      题目集四-1:

            一开始拿到这道题是不知道从哪下手的,因为之前没做过菜单计价程序的1和2,最终代码体现也没写出多少,这也给我写题目集6带来了一定的难度;写这个题目集的时候,确实对这道题掉以轻心了,当时写这道题的时候,就没有写下去的信心。后续放在总结里说明!

      题目集五-5:

       该题的类图:

 

 根据这道题目分析,这道题的编写是有顺序的,要按照类图的线从year开始,一个类一个类按顺序来,不能混着,类与类的联系很强烈,

Project Name 分析
Checkpoint Name Baseline
File Name Main.java
Lines 78
Statements 55
Percent Branch Statements 20.0
Method Call Statements 45
Percent Lines with Comments 5.1
Classes and Interfaces 1
Methods per Class 1.00
Average Statements per Method 51.00
Line Number of Most Complex Method 6
Name of Most Complex Method Main.main()
Maximum Complexity 12
Line Number of Deepest Block 64
Maximum Block Depth 5
Average Block Depth 2.96
Average Complexity 12.00

--------------------------------------------------------------------------------------------
Most Complex Methods in 1 Class(es): Complexity, Statements, Max Depth, Calls

                     单一职责+聚合

      题目集五-6:单一职责+迪米特+控制类

这是该题的类图:

根据产生的类图来看:圈复杂度还在合理范围之内,说明代码冗余还不算严重,相比较第五题来说,关于年月日的方法更多的放在DateUtil这个中介代理类里面,而且不用一级一级的调用,使得代码变得很长,别人还不好理解,而这个类图就可以看出每个类都比较独立,耦合度较低,在编写的时候容错率比较高,我在编写的时候先编写了几个简单的小类,把类里的方法都实现了,再去观察dateutil里的方法。

 

 题目集六-1:

 这个图是我自己生成的,主要的方法和判断都在主方法里面,所以类图比较简单,看到那个题目的时候,我一开始完成了几个类的设计,之后把所有的判断都放在了主方法里,导致了后期编写比较困难。

 

 方法在生成的图可以看出,代码的复杂程度过高,已经远远超过合理范围,这也确实是我需要改进的地方,设计的太过于复杂

三.踩坑心得

      题目集五-5:

           问题一:

           这是日期类的迭代的最后一次,根据产生类图的关系,可以看出是逐级聚合,当我提交过后,测试点显示月份数组越界,这是提交之前的代码,我同时判断日期是否合法,月份是否合法,年是否合法。因为题目中的类图月份最大天数的数组是12个,但是这并不能对应数组中的索引,索引是从零开始的,但是当我输入0月或者13月等临界值时,他会按顺序判断,先判断我的day validate函数是不是正确的,(代码如下)当运行到mon_maxnum[month.getValue()-1]时,他会自动把值减一,从而找不到数组里的索引,导致代码出现问题,而不是为wrong format ,而且在编译的时候我的idea是不报错的,如果不是看到pta的测试点,我可能自己根本找不到这个问题。

 1     public boolean validate() {  //校验合法性
 2         if(month.getYear().isLeapYear()){
 3             mon_maxnum[1] = 29;
 4         }
 5         if (value < 1 || value > mon_maxnum[month.getValue()-1] ) {
 6             mon_maxnum[1] = 28;
 7             return false;
 8         }
 9         else{
10             mon_maxnum[1] = 28;
11             return true;
12         }
13     }

          这是第一次编写的代码:同时判断

          这是修改过之后的判断合法性,我先进行月份合法性的判断,返回true之后再往下进行判断。

 1     public boolean checkInputValidity() {
 2         if(day.getMonth().validate()) { //先判断月是否合法否则数组会越界
 3             if (day.validate() && day.getMonth().getYear().validate()) {
 4                 return true;
 5         }
 6          else {
 7             return false;
 8         }
 9        }
10         else {
11             return false;
12         }
13     }

             问题二:

     个人认为这个问题就是为了让我们体会这道题和下一道题目之间的区别,在编写DateUtil这个类中求下n天函数的时候,我不知道如何比较是不是day类数组里的值,由于数组是私有属性,又不能使用static变量调用,寻找了多种方法,也尝试了在写一个数组进行判断,但是考虑到这样写题目里的数组就失去了意义,所以在和同学讨论了以后,决定采用中间赋值的方法,即先把当前天数的值赋给一个自定义常量,然后再使用resetmax方法,是天数变成当月最大值,然后再进行两者的比较,分类判断之后,再将之前自定义的值用set方法把它赋值回去,(如图这是判断除了12月之外其余每个月最后一天的例子)。

1  int dd = day.getValue();
2             day.resetMax();  //通过中间赋值,在判断该月最大天数,
3             if (day.getValue() == dd && day.getMonth().getValue() != 12) {
4                 day.setValue(dd);  //将天数赋值回去

       题目集五-6:

    有了上一道题的基础,这道题在编写一些函数的时候一较快,基本可以调用,但是仍然不可避免的是遇到了下面的问题;

       问题一:

带参构造的问题(这个其实在第五题的时候也遇到了,放在这里一起说),在编写这个方法的时候,我按照固有思路,把带参构造按如下编写,发现运行完以后结果为空,也就是没有输入进去,查找过后,因为在DataUtil类中属性根据聚合关系是其他类,所以应该先创建。

1  public DateUtil(int y, int m, int d) {
2         this.year = y;
3         this.month = m;
4         this.day = d;
5     }

这是修改过后的代码,在方法中new创建一个新的对象,将值赋到里面。

1 public DateUtil(int y, int m, int d) {
2         year = new Year(y);
3         month = new Month(m);
4         day = new Day(d);
5     }

         问题二:

           数组改变值问题,在一开始编写的时候,我没有第18行代码,然后再测试用例的时候,发现少了好几天,因为数组的值改变了以后是无法再变回去的,也就是说当第一次碰到闰年的时候,mon_maxnum[1]变成了29之后,值就不会改变了,之后的每一年2月都是29天,所以我在最后当每一天加完了以后,重新将2月最大天数定义为28天,从而避免了这个问题。

 1  public DateUtil getNextNDays(int n) {  //获取下n天
 2         for (int i = 0; i < n; i++) {
 3             if(year.isLeapYear()){
 4                 mon_maxnum[1] = 29;
 5             }
 6             if (day.getValue() == mon_maxnum[month.getValue() - 1] && month.getValue() != 12) {
 7                 month.monthIncrement();
 8                 setDayMin();
 9             }
10             else if (day.getValue() < mon_maxnum[month.getValue() - 1]){
11                 day.dayIncrement();
12             }
13             else if (month.getValue() == 12 && mon_maxnum[month.getValue() - 1] == day.getValue()) {
14                 year.yearIncrement();
15                 month.resetMin();
16                 setDayMin();
17             }
18             mon_maxnum[1] = 28;
19         }
20         return this;
21     }

          问题三:

     输入顺序的问题。根据类图可以看出年,月,日的输入是有顺序的,我当时并没有注意这个顺序,结果在写这个时候将日和年搞反了,导致输入一直不正确,然后我用sout语句一直输出,来检查到底是进了哪一个判断语句,然后我发现后输入的年的判断有问题,之后我对比了一下先输入的年的判断,发现只有顺序不同,我就是试着修改了一下顺序,结果运行成功了。(代码如下)

1 int year1 = input.nextInt();
2             int month1 = input.nextInt();
3             int day1 = input.nextInt();  

    题目集六-1:

        问题一:

        图片中被注释掉的字体是我原来编写的添加方法,是用来判断菜品是不是在菜单里的步骤,但是运行时会报错误,当他不存在的时候,我会判断他为空,然后返回一个空值,他就会指向空,无法运行,所以当我写的时候我就把这个判断放在了主方法里面,让他直接输出。

     改动如下:

      利用标记点flag2判断是否存在菜品,当它等于0时,输出不存在,当flag等于1的时候,再往里加入点菜单,然后再每一次点菜记录的循环之后,都要让flag2重新等于0,避免碰到一个不存在的菜之后,后面全部按不存在输出。

 1  if (Integer.parseInt(count[2]) <= 3 && Integer.parseInt(count[3]) <= 15) {
 2                                 for (int i = 0; i < flag1; i++) {
 3                                     if (count[1].equals(menu.dishs[i].name)) {
 4 //                                    System.out.println(count[1] + " does not exist");
 5                                         flag2 = 1;
 6                                     }
 7                                 }
 8                                 if (flag2 == 1) {
 9                                     order.addARecord(Integer.parseInt(count[0]), count[1],
10                                             Integer.parseInt(count[2]), Integer.parseInt(count[3]), menu);
11 
12                                     System.out.println(Integer.parseInt(count[0]) + " " + count[1] + " " + order.records[number].getPrice());
13                                     number++;
14 
15                                 } else if (flag2 == 0) {
16                                     System.out.println(count[1] + " does not exist");
17                                 }
18                             }
19                             flag2 = 0;
20                         }

 

四.改进建议

       通过菜单类的习题,我觉得我还是存在问题,写的时候太过着急了,没有很好的很认真的思考,而是选择了边写边想,这样其实对后续的编写不太友好,而且一旦修改起来,及其麻烦。还是应该先构思好整体方向。并且学会简化代码,提升代码的可读性,理解性,通过题目集5,6的比较,明确了6的编写规则比5更好,代码更简洁,更规范,不容易出现问题。

五.总结

      经过日期类不断迭代的改进,从简单的求下一天开始到上一个随笔里关于7-3,7-4的分析,尤其对比这两道题的分析,让我学习到了类与对象封装性单一控制原则,迪米特法则,以及控制类, 通过这两道题代码的对比,让我感受到了迪米特法则的作用,简单清晰的就可以把数据调用出来,不用像第五题那样比如要调用年先得把月,日get出来,不仅使得代码更为复杂,容易出错,还更不容易让人看懂。关于题目集六拿的分数并不高,确实,一开始看到这个题,自身的想法就有些发怯,没有把写满分作为目标,再后来写的过程中发现,这道题的难度其实并没有很大,只是在判断上有些麻烦。这道题写的比较随意了,也没有狠下心来。但是这个题我写的时间蛮久的,还是欠佳思考,应该吸取教训,摆正自己的态度去迎接后面的练习。