第四单元实验总结 | TrickEye

第四单元实验总结 | TrickEye

基本情况部分

  • 这篇帖子为什么会在这?

    • 这是北航计算机学院面向对象构造与设计2022春季课程第四单元的总结博客

  • 这次作业的要求是什么?

    • 使用面向对象的思想实现对Uml类图、协作图和状态图建模,并在建模完成后进行基本的合法性检查,按要求完成图状态的查询。

第四单元架构设计思维:

本单元涉及的Uml图种类有类图,状态图和协作图。三者之间有各自相应的建模策略,但是主要还是依托图论来实现的。

类图方面,每一个类或者接口可以视为图中的一个节点,彼此之间的泛化,实现和关系可以被视为图中的一个无向边,联系关系可以被视为一个有向边。类内部的一些属性,例如attribute,operation等被视为节点自身的一个属性。因此建模阶段可以被视为是一个图的增与改,合法性检查和指令查询阶段被视为一个图的查询,本次作业中不涉及图的删。

状态图方面,各个State之间的转移是依托于Transition来实现的,因此相当于是各个状态作为节点,Transition视为边。对于状态图的查询被视为了对建模之后的有向图的状态与属性查询。

协作图方面主要涉及各种Lifeline之间依托Message的交互。注意到Message有自己的种类,发送的时间有先后顺序,创建消息和销毁消息还可能对Lifeline造成影响。所以应当注意虽然可以用图论的思想来建模,但是在检查阶段应当注意合乎现实意义。

观察课程组提供的经过建模之后的UmlElement,我们可以发现UmlElement只记录了自己的状态和属性,对于一些和其他UmlElement产生交互的字段,则是通过id这一每个元素独一无二的字段来实现索引的。得益于Java简单好用的哈希映射类,我们可以轻松地通过id来快速找到对应的元素,并通过自己封装的MyElement对应类,在建模时找到产生关联的其他元素并对产生关系的当前元素和其他元素的对应信息加以维护。这也是使用自己的类来对Uml图建模的意义所在。

使用图论的相关算法也可以比较简单的解决要查询的许多问题,比如,要找出类实现的所有接口,所有子类,或者判断一个状态是否是关键状态,就涉及到图的遍历和搜索。判断是否有循环继承,就涉及了找出有向图的环。

具体而言,采用了分层次建模的策略,建模类图的步骤可以归纳为以下几步:

  • 先建立一些独立的概念,例如Class和Interface

  • 随后可以属于Class和Interface内部的概念,例如Operation

  • 随后可以建立Class和Interface之间的关系,这一阶段包括Generalization, Interface Realization, Association等。

协作图有以下几步:

  • 首先找到有没有Collaboration

  • 随后建模Interaction和Endpoint

  • 随后建模在Interaction概念之下的Lifeline

  • 随后建模涉及多个Lifeline的Message,以及Lifeline之下的Attribute

状态图有以下几步:

  • 首先找到有没有State Machine

  • 随后建模Region

  • 随后建模Region概念之下的Pseudo State, State, Final State.

  • 随后建模不同State之间的Transition

  • 随后建模Event

总的来说,本单元因为数据都是人为设置的,元素数量也不是很多,要解决的也是实际的问题,因此在复杂度上宽容度很高,不用刻意针对某个点进行优化,但是有必要对各种情况的正确性做好测试。比如有些复杂的循环继承和重复继承。

架构设计思维与OO方法理解的演进

不得不说面向对象是一个又大又空的概念,他从顶层出发为解决生活中的实际问题提出了方法,提出了一系列设计模型为编程者提供方便也同时约束了代码的“行文思路”。他从底层出发体现在我们写的每一个封装保护字段的private上面。为了讲好面向对象这个概念,北航的面向对象编程与设计课程组通过4个单元的训练来让我们慢慢体会OO的思路。

Tenet里面说过了:

Don't try to understand it. Feel it.

那么在OO的4个单元里面,我是怎么Feel的呢?

Pre-面向对象入门与正则表达式

Pre里面给出了一个应当利用面向对象思想来解决的问题的最简单的形态——直截了当地告诉你应该设置哪些类,这些类中应该设置什么属性和方法,在什么时候应该调用什么对象的什么方法等等,类之间有什么样的关系。这让我们第一次见识了面向对象的魅力:像真实生活中一样建模,让类来完成对内存空间的管理,像真实生活那样解决问题。

第一单元-表达式化简

表达式化简这一单元可以算得上是面向对象给我们的下马威。相比于Pre,形势突然就变得不明朗了起来,要仔细谨慎地思考应该设置什么样的类,类里面需要设置什么样的属性。如果不经过思考直接上去开团,非死即伤。经过了助教发布的课下单元练习和课上实验评测,我才学会了递归下降这一比较好用的思路。

当然了,面向对象的思想下的每个操作内部仍然是面向过程的。如何利用现有的数据结构,比较方便取巧地完成拆括号和化简问题,保证正确率的情况下还要追求尽可能短的表达式长度,那么必然会引入一些不太好看不太工整的“面向过程”

第二单元-电梯

多线程的执行诡异,调试困难,但是我们仍然需要多线程,来解决一些涉及及时性的问题。在本单元的例子中,电梯的请求时异步的,随时的,但电梯线程在请求被输入的时候很可能在sleep,不能及时响应。这个时候如果是单线程就不得不等到进程醒来之后再做其他操作。而多线程就可以设置一个总是能及时相应的线程来处理异步输入的请求。

相比起面向对象而言,电梯的调度策略更像是面向过程。往往需要选择合适的调度策略来满足时间的要求。在这里我吃了不少的亏。

第三单元-JML

形式建模语言从方法的层次上,严格约束了一个方法的具体行为。在设计的时候可以先给出JML描述,以便后期来实现。这样一方面提供了自然语言无法达到的准确性,另一方面也对方法的复杂度做出了约束(因为复杂的方法很难写JML)这在面向对象的编程中有很大的作用。

当然也不应该只学习JML,只写JML,因为在有些场合下,JML不一定比自然语言高效,例如,JML就不能很好地描述最小生成树、最短路等图论里面比较复杂的概念,这种时候选择自然语言,加一句注释或许会有更好的效果。在另外的一些场合,JML有可能限制了实现此方法的编程者的思路,某种程度上增加了复杂度。

第四单元-UML

UML是面向对象的集大成者,它可以对一个面向对象问题的解决思路做出建模,或是对一个面向对象工程建模,明确地描述了类、属性、方法、关系等等。而本单元作业的要求则是通过各种UML元素来使用面向对象的思想建模,完成查询和检查。

通过完成作业,我也逐步更了解了泛化、实现和联合之间的区别与联系。了解到了这么大一张UML图实际上也是由一系列小的基本对象来构成,这也是面向对象的思想:抽象、分解、细化,面向每一个对象。

测试理解与实践的演进

OO这门课有一个很著名的错觉,那就是中测过了强测就能过。在很多情况下,中测的强度都不能检查出所有问题,测试必不可少。哪怕自己写的程序一点问题都没有,互测Hack别人也是依托高强度,精准的测试来实现的。

要做到人工测试与自动测试相结合并不容易,第一单元尚且能根据表达式树逆向地生成一些又臭又长的复杂表达式,也能根据一些常错的情况安排一些人工测试数据。但是第二单元和第三单元数据的编造难度开始上升,更多地依托白盒测试:关键方法的实现上有无错误,性能有无缺陷,等等。

评测机当然是好的,但是说来惭愧,自己搭建评测机比较困难,费时费力,不太有时间。希望以后的其他课程能做好测试。

课程收获

一学期的面向对象也没让我找到对象。

经过了面向对象的洗礼之后,我的代码质量有了明显的提升,解决问题的能力也随着一次次强测和互测有了很大的进步。单从这点来看也是可歌可泣了。虽然我的强测性能不一定是最好的,互测得分不一定是最高的,但总归是有了不少收获。发现了不少bug,也解决了不少bug。

从思想上来讲,面向对象课程告诉我要从设计模型中找到解决问题的思路,不宜目空一切地另辟蹊径,也不宜循规蹈矩沿着前人的脚印走路。

从实践上来讲,面向对象课程告诉我可维护性高的代码的重要性,告诉我要迭代开发,不要重复造轮子,告诉我在写代码的时候不能只贪图一时爽快而不注重可维护性和可拓展性。

改进建议

希望课程组在Pre阶段或者半期阶段新开一章讲讲具体的Junit测试过程,因为虽然课程组一直在推荐我们使用Junit来做测试,但是测试方法都需要我i们自己在网络获取,不一定能找到简单高效易上手的Junit教学。

希望课程组在电梯单元适当放松强测对时间的限制,因为时间长点只是性能差点而不是正确性有错误。如果把时间稍长就视作为错误会降低学生对不同调度策略的探索积极性。局限在LOOK算法对于面向对象的学习不是一件好事。

希望能提供更好的研讨课引导,目前而言研讨课容易出现大家都讨论一样的东西,小组上台发言内容相互撞车,或是小组讨论言之无物的情况。例如,比起简单地让我们讨论“设计模式有什么好处?”就比较言之无物,不如给我们丢几段设计模式不同,解决相同问题的代码,让我们比较优劣。

posted @ 2022-06-29 11:44  TrickEye  阅读(24)  评论(0编辑  收藏  举报