面向对象第四单元作业个人总结与反思

面向对象第四单元作业个人总结与反思

架构设计与代码分析

由于本次的对JML的保存及请求酷似MongoDB之类的非关系数据库及对其询问,因而我便突发奇想,将我的整个代码设计成了一个「偽」数据库。

我的代码一共分为两层;顶层的业务层(负责实现UMLGeneralInteraction的接口,并将其转换为底层的查询)以及底层的服务层(以ORM的方式储存数据、提供数据,并且提供基础的查询服务)。这样的划分使得我的代码层次分明,有条不紊,为我的设计带来了莫大的帮助。

UML上大致的设计如下图:

Sora

在细节实现上,遵循着张家树助教在研讨课上给出的指引,我学习了java8中的新特性:函数式/流式编程。流式编程具有可读性强,操作简单(虽然感觉说操作meta更精确一点),并且很好玩的极佳优点。谁又能抗拒一直.下去直到得到结果的爽快呢?

举个例子:

List<AttributeClassInformation> ret = new ArrayList<>();
List<MyUmlAttribute> attributes = otoDatabase.getCheckedClass(className).getAllAttributes();
for (MyUmlAttribute attribute : attributes) {
    if (!attribute.getVisibility().equals(Visibility.PRIVATE)) {
        ret.add(new AttributeClassInformation(attribute.getName(),
                attribute.getParent().getName()));
    }
}
return ret;

这是一段获得类中非隐藏属性的代码。虽然可读性比较好,但是两层的大括号嵌套仍然过于复杂,并且让我这个强迫症感到难受。

改成流式编程后如下:

return otoDatabase.getCheckedClass(className).getAllAttributes().stream()
    .filter(attr -> Visibility.PRIVATE.equals(attr.getVisibility()))
    .map(attr -> new AttributeClassInformation(attr.getName(), attr.getParent().getName()))
    .collect(Collectors.toList());

通过一些预定义好的Meta函数(没有函数式编程理论基础,所以不确定具体应该称做什么),如上面的filter、map等,再加上我们传入的lambda表达式,就可以明确的完成相同的任务,并且直截了当了显示出了数据的处理过程。什么?你说不等价?恭喜你,我强测就挂在这了。所以说研讨课是万恶之源

实际上这种流式编程的方法结合对lambda函数、各函数接口的合理使用,是可以合理替代java语言中的继承、多态等核心内容的。

回到架构层面。在业务层,我基本使用了如上面代码一样的方式来完成作业要求,所有方法基本没有超过十行。当然,为了完成这个目标,我也写了一些工具类(如图遍历、检查器、以及各式小玩意);而在数据层,我将所有的数据(继承自官方包UMLElement的“JavaBeans”)都进行了封装,变成了类似于“ORM”,能够提供对应查询接口(如查询方法的所有参数、查询类的父类)的数据类。

这里盗一张我们研讨课上的图:

に届けて

最后在加上一个使用了流式编程的builder(应用了没啥大用的建造者模式,负责建立整个数据库的数据及关系),我的架构就完善了。

架构设计及OO方法理解的演进

  • 在Pre中,我凭借我已有的孱弱的Java经验随便写写C-like的代码,以及稍微分两个类就算解耦合的态度,轻松的完成了作业;
  • 在第一单元中,在研讨课上接触了设计模式,成功应用了组合模式和工厂模式;
    • 读了本关于设计模式的书,大概了解了一些SOLID之类的思想;实际上在后几次作业中逐渐加深了认识,使我受益匪浅。
    • 此外还通过博客作业理解了降低程序复杂度对于提高其鲁棒性的重要性。
    • 看了两三篇博客园博客,决心着手降低代码复杂度。
    • 决心搭起一个好的架构,毕竟第二次作业重构了。
  • 在第二单元中,在课上了解到了各种多线程设计方法,根据指导书推荐和已有知识选择了策略模式和状态模式进行应用;
    • 由于没有多线程经验,因此十分小心。好像没犯什么多线程错误。
    • 发现了状态模式在多线程环境下的问题。
    • 从策略模式中体会到了合理命名、ODP、javadoc等的重要性(具体见第二次作业长文总结),因为单单接口阻止不了我写出耦合的代码。
    • 理解到了开发一个工程所需要的周期。
    • 研讨课上推荐的JProfiler要钱,选择了室友班研讨课介绍了开源的virtualVM。
    • 决心搭起一个好的架构,毕竟第二次作业重构了。
  • 在第三单元中,在课上了解了JML的充足的知识,随即展开实践。
    • 由于对图的性质比较熟悉,因此直接套用了链式前向星的板子。最后也并没有造成什么麻烦。
    • 并查集之类的算法基本都应用上去了。
  • 在第四单元中,在课上主要了解的是UML,并没有涉及架构;但研讨课上被介绍了流式编程
    • 将我的代码设计成了上面介绍的数据库-业务的分层架构;
    • 广泛使用了流式编程。
    • 决心搭起一个好的架构,毕竟第二次作业重构了。

这四次作业是我不断设计、不断学习、不断重构的过程;每一次重构都代表着我对OO以及JAVA的认识的进一步加深。(以及我挂强测可能性的升高)

受益匪浅。

测试理解与实践的演进

  • 第一次作业中随大流写了一个自动评测机,很好用。
    • 这时候并没有什么工程意识,因此写出的代码一点也不OO,也不具有系统的测试能力;
    • 这时候或许黑箱测试是一个正确的选择。
    • 应用了一些技术,不过没什么可说的。
  • 第二次作业中仍然是通过评测机来验证。
    • 与第一次作业相似,除了没有工程意识之外,对性能分的病态般的追求也使得我写不出一个我认为的好代码。
  • 第三次作业中开始应用JUnit。
    • 第一次作业中应用了JUnit4,完成了全覆盖率检查。还是比较满意的。
    • 第二次作业中出于对异常检查的需求,升级到了JUnit5。
    • 理解了高覆盖率的单元检查对于系统健壮性有着极大提升。
    • 理解了测试在工程中绝对是非常重要、必不可少的一环。
    • 理解了系统正确性不是不可把握的东西。(虽然很难把握),改变了我长久以来被评测机惯的轻视测试的观念。
    • 理解了交叉测试的重要性——你没读懂题时写出来的测试代码也是错的。
  • 第四次作业中自己构造了大量样例进行类似于JUnit的100%覆盖率debug。
    • 结果比较完美。

测试是工程周期中不可获缺的一环。为了追求系统的正确性与健壮性,我们需要测试。

写出没有bug的代码是天方夜谭,但是整理出没有bug的代码是可行的。

自己的课程收获

  • 上面都写的差不多了,我这也没啥可说的了……

  • 熟悉了一整套从需求分析到测试验收的开发周期(见第二次博客);

  • 基本熟悉了一遍设计模式,开始理解并追求SOLID;

  • 懂得ODP,正在尝试消化AOP;

  • 打开了函数式/流式编程的大门;(虽然之前会点common lisp,但是没实际应用过)

  • 多线程技能树向上点了一层。学习或复习了锁、各种锁、callback、Promise等等。

  • get到了JML、UML以及其配套工具;

  • 理解了诸多设计模式,并且实际应用了好几个;

  • 每个周末都过的极其特别充实;

  • 感觉自己确实开始真正接触java理论及其周边了。

  • 一些遗憾:

    • 仍然没有应用到注解、反射这两个重要的语言功能;
    • 没有一次作业的架构达到或接近了framework级别(这可能达到吗)
    • 有些优化想法没能实现,毕竟这是课程而不是playground。

三个建议

  • OO这门课程有一个特质十分明显:任务量大,可扩展任务量更大。因此多人协作得到的分数肯定要比一个人高。如何合理利用讨论区、研讨课,为单独完成作业的同学提供辅助,或许是一个可以尝试的方向。目前OO课程并没有对编程小技巧这种提供一个交流平台/发言机会,或许有改进的空间。
  • 互测可能并不会起到互相学习架构的效果。可能推荐博文/优秀博文可以弥补一些这个功能。
  • 我热衷于尝试从研讨课、课上得来的新想法,愿意去重构代码;但是这会使我无暇顾及测试,以至于甚至挂掉强测。虽然提不出更好的建议,但我觉得OO测试分数和最终分数强挂钩的行为损害了我的积极性。我总有一种挥之不去的念头,觉得我学到了很多,水平也还不算差;但就是拿不到分数。

最后,OO终于结束了!!!

虽然不太合乎时宜,但是还是想说一句:
()
()
()
()
!!!

bgm:黎明的三棱镜

之后也希望能够继续探索OO和java~ (ことよろ)
posted @ 2021-06-23 22:14  tadshi  阅读(132)  评论(2编辑  收藏  举报