OOP第二次总结

OO作业第4-6次总结

一、前言

​ 本次总结是OO的第二次总结,相比第一次来说,习题更多是关于类的设计以及正则表达式的应用,还涉及到了字符串和数组和数组列表等其他知识点,可所谓全方位考察。题量虽然不多,但是难度还是有,每个题目都需要谨慎的长时间思考后才能做出来,所以整体来说作业适中,比较符合我的预期。而题目难度也是递进,在实现同一个功能的程序却使用了不同的方法,符合了程序的改进以及复用性。

二、设计与分析

  1. 题目集4 7-2和题目集5 7-5日期类设计的比较

    ​ 题目7-2的类设计更多的体现了类的组合关系,年的实例随着月的创建而创建,月也随着日的创建而创建,而日随着日期单元的创建而创建,这四个类具有相同的生存期,也就是java中的类组合。这样做的好处是更能紧密的把类结合起来,最主要的体现是类的嵌套,更像是一个类包含另一个类的关系,月份实例可以包含多个年份实例,而日期实例又能包含多个月份实例。

    ​ 题目7-5的类设计则是一个日期单元包含年月日三个实例,虽然也是组合关系,也有相同的生存期,但是这个不同的是年月日三个实例对象是平行的关系,不是包含的关系,这样做才更能体现日期单元和年月日的关系。

    ​ 7-2的优点在于类之间的复合比较紧密,但是调用方法时需要层层进入,可能会导致栈溢出,而且调用的时候很麻烦,7-5则是更符合题意的类关系,可以更方便的调用方法。

    ​ 使用UML类图则更能直观的感受:

    ​ 题目7-2:

    p

    ​ 题目7-5:

    ​ 根据类图可以看出之间的类关系

    ​ 复杂度分析:

    com.Main.DateUtil.checkInputValidity() 1 1 3 3
    com.Main.DateUtil.compareDates(DateUtil) 7 5 5 5
    com.Main.DateUtil.DateUtil() 0 1 1 1
    com.Main.DateUtil.DateUtil(int,int,int) 0 1 1 1
    com.Main.DateUtil.Dayvalidate() 14 1 14 14
    com.Main.DateUtil.equalTwoDates(DateUtil) 1 1 3 3
    com.Main.DateUtil.getDay() 0 1 1 1
    com.Main.DateUtil.getDaysofDates(DateUtil) 17 4 7 11
    com.Main.DateUtil.getMonth() 0 1 1 1
    com.Main.DateUtil.getNextNDays(int) 3 1 3 3
    com.Main.DateUtil.getPreviousNDays(int) 4 1 3 3
    com.Main.DateUtil.getYear() 0 1 1 1
    com.Main.DateUtil.setDay(Day) 0 1 1 1
    com.Main.DateUtil.setDayMax() 2 1 3 3
    com.Main.DateUtil.setDayMin() 0 1 1 1
    com.Main.DateUtil.setMonth(Month) 0 1 1 1
    com.Main.DateUtil.setYear(Year) 0 1 1 1
    com.Main.DateUtil.showDate() 0 1 1 1
    com.Main.Day.Day() 0 1 1 1
    com.Main.Day.Day(int) 0 1 1 1
    com.Main.Day.dayIncrement() 0 1 1 1
    com.Main.Day.dayReduction() 0 1 1 1
    com.Main.Day.getValue() 0 1 1 1
    com.Main.Day.setValue(int) 0 1 1 1
    com.Main.Main.main(String[]) 11 1 5 8
    com.Main.Month.getValue() 0 1 1 1
    com.Main.Month.Month() 0 1 1 1
    com.Main.Month.Month(int) 0 1 1 1
    com.Main.Month.monthIncrement() 0 1 1 1
    com.Main.Month.monthReduction() 0 1 1 1
    com.Main.Month.resetMax() 0 1 1 1
    com.Main.Month.resetMin() 0 1 1 1
    com.Main.Month.setValue(int) 0 1 1 1
    com.Main.Month.validate() 2 1 2 3
    com.Main.Year.getValue() 0 1 1 1
    com.Main.Year.isLeapYear() 2 1 1 3
    com.Main.Year.setValue(int) 0 1 1 1
    com.Main.Year.validate() 2 1 2 3
    com.Main.Year.Year() 0 1 1 1
    com.Main.Year.Year(int) 0 1 1 1
    com.Main.Year.yearIncrement() 0 1 1 1
    com.Main.Year.yearReduction() 0 1 1 1
    Total 66 49 81 92
    Average 1.571429 1.166667 1.928571 2.190476

    可以看出,在求日期之间的天数上,还是占用了较高的认知复杂度和圈复杂度,主要是利用了大量的求和语句来比较天数。

  2. 题目集4 7-3 题目集6 7-5 7-6 设计思路和技术运用

​ 这三道题都涉及到了关于封装、继承、抽象、接口的知识,层层递进。

​ 题目7-3主要运用了继承的功能,圆形和方形继承形状类,球类和盒子类分别继承圆类和方形类,这样做更好的体现了类之间的关系,继承的运用主要在于子类可以调用父类中的方法,在获取面积这个方法上由于圆的面积求法和方形不同,但是都需要求面积,所以分别继承,结构合理。在主方法中只需创建相应实例就能调用相应的求面积方法。

​ 题目7-5的题目要求更多,还需根据面积进行排序,所以将图形存入一个数组列表中,这样方便排序。最主要的方法运用在继承的基础上又增加了抽象、多态的运用。首先,抽象的使用改进了形状类,由于形状没有具体实例,将它变成抽象类更加符合他的作用,而且他的求面积和合法性检验的方法在每个类中都需要使用,抽象类的应用在这里体现。而多态性的使用则让实例对象统一到形状类里,但是却又有各自的方法,这样做方便管理和操作数据。在主函数中则只需要创建图形实例并添加到数组列表中,调用时使用每个实例的具体方法,方便求和。

​ 题目7-6的题目则要求将求面积的方法封装在接口中,接口也是一种抽象的体现,但是一个类可以使用多个接口但是只能有一个父类,接口更像是功能的实现,不具有is-a的关系。

​ UML类图:

​ 题目7-3:

​ 题目7-5:

​ 题目7-6:

​ 可以看出类图越来越简约,类之间的关系也简化了。

​ 复杂度分析:

com.Main.Circle.Circle() 0.0 1.0 1.0 1.0
com.Main.Circle.Circle(double) 0.0 1.0 1.0 1.0
com.Main.Circle.getArea() 0.0 1.0 1.0 1.0
com.Main.Circle.getRadius() 0.0 1.0 1.0 1.0
com.Main.Circle.setRadius(double) 0.0 1.0 1.0 1.0
com.Main.Circle.toString() 0.0 1.0 1.0 1.0
com.Main.Circle.validate() 0.0 1.0 1.0 1.0
com.Main.Main.insertionSort(ArrayList) 6.0 1.0 4.0 4.0
com.Main.Main.main(String[]) 10.0 1.0 9.0 11.0
com.Main.Rectangle.getArea() 0.0 1.0 1.0 1.0
com.Main.Rectangle.getLength() 0.0 1.0 1.0 1.0
com.Main.Rectangle.getWidth() 0.0 1.0 1.0 1.0
com.Main.Rectangle.Rectangle() 0.0 1.0 1.0 1.0
com.Main.Rectangle.Rectangle(double,double) 0.0 1.0 1.0 1.0
com.Main.Rectangle.setLength(double) 0.0 1.0 1.0 1.0
com.Main.Rectangle.setWidth(double) 0.0 1.0 1.0 1.0
com.Main.Rectangle.toString() 0.0 1.0 1.0 1.0
com.Main.Rectangle.validate() 1.0 1.0 1.0 2.0
com.Main.Shape.Shape() 0.0 1.0 1.0 1.0
com.Main.Triangle.getArea() 0.0 1.0 1.0 1.0
com.Main.Triangle.getSide1() 0.0 1.0 1.0 1.0
com.Main.Triangle.getSide2() 0.0 1.0 1.0 1.0
com.Main.Triangle.getSide3() 0.0 1.0 1.0 1.0
com.Main.Triangle.setSide1(double) 0.0 1.0 1.0 1.0
com.Main.Triangle.setSide2(double) 0.0 1.0 1.0 1.0
com.Main.Triangle.setSide3(double) 0.0 1.0 1.0 1.0
com.Main.Triangle.toString() 0.0 1.0 1.0 1.0
com.Main.Triangle.Triangle() 0.0 1.0 1.0 1.0
com.Main.Triangle.Triangle(double,double,double) 0.0 1.0 1.0 1.0
com.Main.Triangle.validate() 1.0 1.0 1.0 6.0
Total 18.0 30.0 41.0 49.0
Average 0.6 1.0 1.3666666666666667 1.6333333333333333

​ 主要是在主函数的添加方法使用了大量的for语句增加了好多圈复杂度,还有不同的计算方法也增加了设计复杂度。

  1. 正则表达式的分析

​ 本次三次作业都有相应的练习正则表达式的题目,可以体现出正则表达式在检验字符串上的重要作用,但是正则表达式比较难掌握,需要多加练习才能有所领悟。

​ 题目集4 7-1的题目是一个很好的练习正则表达式的题目,题目与实际生活相结合,题目也有一定的难度。水文数据的校验比较复杂,首先将多行水文数据利用换行符分割成一条条的水文数据,其次数据需要用|分成五个部分,每个部分都需要用表达式检验。正则表达式通过匹配数字、字母、字符等来检验字符串的合法性,水文数据主要是数字的合法性以及长度合法。在长度上,可以使用{}来匹配字符的数量;可以用?来表示一个数据可能出现;\\d则可以表示0-9的数字。

​ 题目集6 题目7-1 QQ号的检验主要检验数字的长度检验 7-3主要是检验字符串的长度以及字母和数字的合法性,\w用来检验字母和数字,题目7-4的学号检验比之前的要复杂一点,但也仅仅是数字检验,在这里就不过多赘述。

​ 题目7-1类图分析:

​ 复杂度:

com.Main.CheckData.CheckData() 0.0 1.0 1.0 1.0
com.Main.CheckData.CheckData(String,int) 0.0 1.0 1.0 1.0
com.Main.CheckData.getData() 0.0 1.0 1.0 1.0
com.Main.CheckData.setData(String,int) 0.0 1.0 1.0 1.0
com.Main.CheckData.toHydrologicalInfo() 0.0 1.0 1.0 1.0
com.Main.CheckData.validateData() 23.0 3.0 10.0 15.0
com.Main.CheckData.validateGateOpening(String) 1.0 2.0 1.0 2.0
com.Main.CheckData.validateMeasureDateTime(String) 0.0 1.0 1.0 1.0
com.Main.CheckData.validateWaterLevel(String) 0.0 1.0 1.0 1.0
com.Main.DealData.computeData(HydrologicalInfo[]) 5.0 1.0 4.0 4.0
com.Main.DealData.DealData() 0.0 1.0 1.0 1.0
com.Main.DealData.DealData(StringBuilder) 0.0 1.0 1.0 1.0
com.Main.DealData.getDealDataResult() 7.0 3.0 5.0 6.0
com.Main.DealData.getSb() 0.0 1.0 1.0 1.0
com.Main.DealData.setSb(StringBuilder) 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.getActualGateOpening() 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.getActualWaterLevel() 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.getMeasureDateTime() 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.getObjectGateOpening() 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.getObjectWaterLevel() 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.getWaterFlow() 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.HydrologicalInfo() 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.HydrologicalInfo(LocalDateTime,double,double,double,double,double) 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.setActualGateOpening(double) 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.setActualWaterLevel(double) 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.setMeasureDateTime(LocalDateTime) 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.setObjectGateOpening(double) 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.setObjectWaterLevel(double) 0.0 1.0 1.0 1.0
com.Main.HydrologicalInfo.setWaterFlow(double) 0.0 1.0 1.0 1.0
com.Main.Main.main(String[]) 1.0 1.0 2.0 2.0
Total 37.0 35.0 47.0 54.0
Average 1.2333333333333334 1.1666666666666667 1.5666666666666667 1.8

复杂度平均都能说得过去,除了合法性检验上有点高,不过这是必要的。

  1. 题目集5 7-4中Java集合框架应用

​ 这道题需要寻找一段源代码中Java关键字的出现字数,需要的不仅仅是找到还需要统计次数,所以很显然的想到了Java中的集合知识,集合中的变量是通过键值对(key-value)的形式存储的。这对于学过python的字典的我来说理解起来不是很困难,但是这两者又有许多的的不同。

​ 键值对可以通过值来寻找键,这样方便了关键字的寻找。但是首先,在输入源码时会有字符串中的内容以及注释中的内容干扰,首先要做到的就是对字符串进行处理,将字符串处理后,利用Map进行储存和搜索。当然由于我对集合的知识还掌握的不多,所以有一些测试点不能通过,希望之后能多加学习后改正。

三、踩坑心得

  1. 题目集4:

    1. 题目7-1

    ​ 题目7-1的坑可是太多了,直到最后我也没从坑里爬出来(指有一些测试点依旧过不去)

    ​ 首先在数据的传递和处理上,就遇到了不小的麻烦,具体数据怎么根据题意传入传出,需要一定的考虑。开始做的时候,我将每条水文数据分成5个小部分,每个部分作为参数传入Checkdata类中,这样导致不能根据数据的编号来检验合理性,而且在其他功能的实现上也有一堆问题,于是重写。。改正后还是将每条数据传入后再分割比较好。

    ​ 其次正则表达式就不说了。。对于刚刚接触正则的萌新来说折磨了好久。。不过最终还是勉强写出来了。

    ​ 再者就是LocalDateTime类的使用。。日期格式不对等等问题(后来发现根本用不到日期),这个类还需查阅更多资料。总之,这个题值得我们去细细品位。

    1. 题目7-2

      这个题主要的问题在于功能方法上出现的小问题:

      1. 首先在实现比较两个日期大小的时候,只考虑到了年份相比较,忘记了年份相等时的问题,于是改正:

        else if (day.getMonth().getYear().getValue() == date.getDay().getMonth().getYear().getValue()){ if (day.getMonth().getValue() > date.getDay().getMonth().getValue()) { return true; } else if (day.getMonth().getValue() == date.getDay().getMonth().getValue()) { return day.getValue() > date.getDay().getValue(); } else return false; }

      2. 在求日期的下n天的功能实现上,由于要将原有日期的基础上加n,测试时会导致整形数据越界,导致错误,所以这里的int days需要改成long days防止数据溢出。

      3. 如果判断前一个日期比后一个日期大,本来我进行的操作是根据不同的情况做不同的操作,增加了许多代码,最后变量太多导致我自己都晕了,于是想到为什么不交换日期。删除了一些冗余的代码,增加了一个交换日期的代码;在最开始的时候我还发现原本的方法不能直接这道题,因为这样做输入的数据就会被覆盖,需要增加变量来保存原来的数据。

      4. 还有一个小问题就是存储每个月份的最大天数使用的是数组,而月份是从1开始,所以索引的时候需要-1。

  2. 题目集6:

    1. 题目7-4:

      ​ 这道题的错误在于一开始正则表达式没有考虑到00的情况和41的情况,于是发现不能简单的写一个[1-4]\\d来检验,需要考虑到当编号小于10时前面是有0的,于是改成(0[1-9])|([1-3]\\d)|(40)来分别检验。

    2. 题目7-5:

      ​ 这道题的问题在于没有考虑到图形的数量为0的情况,导致程序异常退出,事实上当一种的图形数量为0时,其他的图形应该能继续计算。

      ​ 由于我最初使用数组列表时我还不甚了解,所以对如何将其中的对象排序比较模糊,幸好我请教到了高人,指点了我,帮我解决了我的疑惑,其实就把他们看作普通的数,根据面积比大小只需调用其中的方法就可以。

四、改进建议

  1. 题目集4:

    ​ 关于题目7-1的改进,我认为水文数据一般处理的数据比较多,总数可能会很大,可能导致数据溢出,所以我认为应该将储存总数设置为BigDecimal类来储存一个更大的数。在其他的方面,数据处理还做的不是很好,数据之间的传递关系不是很清楚,应当改进。根据solid设计原则,在单一职责方面做的不是很好,一个类做了多个工作,但是这样做减少了数据的传递。OCP原则则是实现的比较好,能够做到添加数据来实现新的功能。

    ​ 题目7-2的改进则是每个类中相同或者相似的方法都可以封装在一个接口中,每个类则可以直接调用接口来实现功能,提高代码可读性和可维护性;在实现功能的方法中,调用了大量for语句圈复杂度比较高,可以进一步优化。而且在读入数据时代码可以封装在方法中。

  2. 题目集5:

    ​ 关于题目7-4的集合接口,先留个位置,以后学会了更多再来修改。

  3. 题目集6:

    ​ 题目7-4中Main方法太过于庞大,而且可读性较差,可以将读入和计算封装在方法中,并按照单一职责修改代码。

五、总结

​ 总之,这次作业我学到了很多,初步接触到了继承、多态、接口、抽象等面向对象编程的内容;还掌握了关于类的聚合以及类设计的知识,学会了根据不同类之间的关系来设计类的结构;还初步入门了正则表达式,学会了写一些基本的正则表达式来检验字符串;还学会了一些零碎的知识,比如LocalDateTime类的使用以及集合的基本知识还有字符串转换成char型数组等等。。至此,面向对象的基本内容已经大致学完,但这只是学习Java的入门,还有很多只是等着我去学习,今后的学习注重于Java的集合接口,实现一些基本的算法等等。我希望老师能讲述一些关于测试程序的内容以便我们自己测试,或者在每次作业结束之后讲述一下PTA的测试点,这样才知道自己错在了哪里。

六、其他

  1. SOLID设计原则:六大设计原则(SOLID) - 简书 (jianshu.com)

  2. 正则表达式:GitHub - ziishaned/learn-regex: Learn regex the easy way

  3. 数据结构和算法可视化:VisuAlgo - 数据结构和算法动态可视化 (Chinese)

  4. 图片引用:

posted @ 2021-04-27 18:16  markbee  阅读(75)  评论(0)    收藏  举报