面向对象第四单元总结

单元作业架构

第一次作业架构设计

首先作业要求我们实现一个UML类图解析器,即按照指定的UML格式输入数据,提取出相应的关键信息以一定的形式存储起来再进行后续的对类图进行查询的操作。

我在此次作业中采用了建立类图模型的方法,将其中的元素提取出来后用各个不同的类来具象化,建立类图模型框架。

主要提取出的特征类有:类、接口、属性、方法。

在外层会有一个整体模型对上述各个不同的组合进行包裹。

类中的属性由于需要大量的搜索查询操作,故考虑优先使用HashMap数据结构来进行存储,加快查询效率。并且必要时,一个数据对象可能需要分别用不同的关键字来存储两次,以空间换时间。

类图如下:

在解析元素时由外层向内层解析,比如如果想要具象化属性、方法等那么前提是其从属的类或接口已经被具象化了,否则是不能实现的,但又由于官方包给出的输入操作是顺序输入,所以必然存在可能使得上述假设成立,那么本次作业为了避免出现上述错误,采用了先全部读取输入到一片内存再按需要取出的方法。对于涉及搜索的函数(例如寻找所有关联的类、所有实现的接口等),采用转入hashmap作为函数参数,递归调用进行修改的方法,最终返回时参数hashmap就已经加入了所有符合要求的元素。

由于递归查询可能会导致递归层次过深而爆栈或是超时等情况,再次用空间换取时间,采用缓存的思想。

缓存可以有多个层次,本次实验中采用的是对每一个类模型建立一个缓存,在递归到本层时,通过标记查看缓存有无被更新,若已经被更新那么可以直接读出缓存内容,而如果没有被更新,则正常进入下一层递归查询。

例如如下方法结构:

public HashMap<String, String> getGeneratedList() {
        if (genSearched) {			// 是否被查询过
            return genSearchCache;  // 直接返回缓存内容
        }
        
        							// 没有被查询过,正常查询
        
        if (parent != null) {
            genSearchCache.put(parent.getId(), parent.getName());
            genSearchCache.putAll(parent.getGeneratedList());
        }
        
        genSearched = true;			// 标记查询
        return genSearchCache;
}

第二次作业架构设计

第二次作业的架构思想与第一次作业类似,但是抽象层次更高了。

由于要对三种类型的图建模,故把每种类型图的模型都放在一个package里面便于整理。

代码的层次结构如下图所示:

UML类图结构与第一次作业相似,但可能复杂度更高了毕竟加了两个模块,互相之间也有不少联系。

相似的是建模的顺序和思路以及在进行查询操作时候设置缓存的方式。

第三次作业架构设计

第三次作业涉及模型的有效性检查,基本架构与第二次相同。

在进行有效性检查时,检查方法与之前的查询方法类似,如果有必要也需要运用递归来进行查询操作。

为了降低时间复杂度,某些有效性检查可以在建立模型是就进行简短的判断处理,以免后续的循环查询。

UML类图结构如下:

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

个人感觉每一个单元都有每一个单元的设计特点,总共下来四个单元让我认识到了面向对象的不同方面的特色。

对于第一单元来讲,不知是因为初次接触面向对象还是作业本身的缘故,使得我对于面向对象设计构造的程序设计方法与以往设计方法的辨析最为明显,同时在迭代开发的过程中还进行了一次作业的重构,可能这就是从面向过程到面向对象的一个清晰的转变吧,逐步地从解析一个整体的多项式表达式到提取关键元素,建立多项式模型再到最后求出导数,化简表达式、返回。整个过程的每一个环节都可以抽象为一个类的功能,当然还有众多其他不同的实现形式和方法,然而设计的重点都在于如何构造一个类的模型。整体来看,第一单元的入门还算顺利,开了一个OO的好头。

第二单元当时的入门对于我来讲真的是非常的不易,对于多线程的设计花费了大量精力才研究明白,个人认为本单元的的重点在于如何设计多线程之间的交互,能够使得线程安全运行,毕竟程序的正确性是重中之重,这涉及到了“锁”的使用、变量的共享、线程间的同步与互斥等等。在保证了正确性之后又有一大难题便是电梯的调度问题,这个问题非常的实际,因为不可能有一种调度算法在任何情况下都是最优的,所以这要求我们不断地去调整参数,找到一种相对稳定的且易于实现的算法逻辑。这一单元好似一个面向对象能力的进阶,让整体程序设计能力上了一个台阶。

第三单元看大家的感受都不错,似乎大家把第三单元当作了一次轻松的练习。JML在本单元的角色主要是充当“代码指导书”,即要学会读懂JML并根据其要求写出相关类中方法的程序代码。要说难点的话,可能在于JML代码有点难啃,阅读起来有些繁复,另外一点就是如何高效实现方法要求。在阅读JML时,最重要的是要深层次理解其设计目标的内涵(如最短路、连通分量数量等),分析可能会用到的数据结构和算法模型,针对特定的JML规格,方法的具体实现方法有许多种,所以应当在算法上多下功夫。

第四单元应当是代码量最大的一个单元了吧,虽说表面上作业是对UML相关模型的解析,但是其实本质上来讲系统的考察了我们抽象建模的能力。本单元的设计的重点应在于层次化抽象成模型。层次化代表着应当将UML图中的各种元素进行层次划分,形成从抽象到具体的逻辑关系。同时在复杂度上又与算法相结合,考察在应用中实践的能力。

测试理解与实践演进

测试方法在四个单元中的整体思路相同,但可能具体单元有一点点相异的地方。

相同点:

  • 反复阅读指导书,找出容易出错的点或者语义理解有误的点进行手动构造测试数据测试。
  • 对于正确答案唯一的程序可以编写数据生成器,和多份代码对拍

不同点:

  • 第三单元利用JUnit进行单元化测试
    • 其实JUnit也可以用在其他单元里面,但是由于该测试方法编写构造量和bug纠察量相比太法,入不付出,所以之后没有再用到该方法。

当然,笔者也抽象地总结了测试程序的方法:

  • 随机法
    • 随即构造或者使用自己编写的测评机随机构造测试样例来进行大规模大批量测试
  • 复杂法
    • 增加测试数据的复杂性和多样性,使得在一个测试数据中出现多种可能相互交叠才会出现的出错机会
    • 增加嵌套深度,考验程序的鲁棒性
  • 分析法
    • 通过分析指导书和程序架构找出容易出错的点进行针对性测试,同时边缘测试的一些极端样例也包含在内

综合上述三种测试方法,性价比较高的方法为复杂法,能够在较短时间内构造出多种错误测试样例。

课程收获

在课程中最最主要的就是学习到了系统化建立程序代码结构的能力,这种能力必须要是从数千行代码的有效训练中才能获取到的。

其中包括了:

  • 对面向对象思想的建立

    通过本课程的学习,我理解了面向对象和面向过程的差异,并掌握了面向对象的基本实现思路和应用场景,能够使用面向对象思想对于某问题进行分析和给出解决方案。

  • 对多线程程序编写和调试的实践

    学习中首次接触到多线程代码的编写,对其中各个线程间的互斥与同步有了初步的一个认知和掌握,但经过了一单元的学习,感觉多线程里的水还是很深,有许多许多的内容我都还没有了解过,这仅仅是一个开始,希望以后会不断地补充。

  • 迭代式开发的体验

    课程中的每一次作业都应用了迭代式的开发,在每一次的代码中都有或多或少的改动,可能与实际的软件开发类似,需要我们在基础建设使其就将代码结构基础打好以避免以后进行重构。

改进建议

  • 对于三、四单元的指导书描述有点点疑惑,而且三四单元的讨论区基本上是爆炸的,很多同学都在讨论区里描述各种各样的问题,希望课程能够结合一些具体的样例来进行描述(不过感觉也不现实,有了样例不就相当于给了一部分答案吗
  • 实验课希望能有一些反馈结果,比如正确性反馈等等。
  • 感觉可以给多线程部分加入一些内容,然后在JML部分减少一些内容,因为感觉JML部分似乎使用的不是那么的普遍,学习过后暂时没有感觉有太大用处。

感想

这门课本学期自己学习的很认真,感觉课程体系很完整,毕竟也经历了多年的改进。对于我个人来讲,不容置疑肯定提升了自己的面向对象的程序设计能力。

总体各个单元作业难度适中在我努力的啃许多遍之后还是能完整的做下来的,总体上很有成就感。

最后的课程总结颁奖看到了课程组的老师们和助教们,看到自己老师还挺亲切的,以及把各个助教的名字和真人也终于对上了。特别感谢老师们和助教们的付出,OO课程一定会在一届届老师和助教的付出中不断地成长!

这也是我面向对象设计的开始,还有很多路要走、很多坑要踩,但不断地学习、进步是不变的。

共勉!

posted @ 2021-06-26 13:11  CpyKing  阅读(72)  评论(0)    收藏  举报