OO Unit4 总结

OO Unit4 总结

总述

本单元任务量相较之下是四个单元里面最小的,既没有互测,n和m都很小所以基本也不需要考虑TLE的问题,对于UML各种图的评测也是仔细看评测详情强测就没有问题,只要把课下的测试做充足,强测出问题的概率非常小。

而本单元课程组希望我们尝试一次从先建模后实现的过程,总体来讲,我也算做到了,虽然有做的不好的地方,但是好在这个单元顺利的结束了。

从建模到开发——一次正向过程的实践

在之前的三个单元中,虽然在第一次作业之前我基本都会仔细想想怎么实现这样一个整体系统,打个腹稿,或者从U2开始会思考架构的可拓展性和性能,但大多数只是有一个大概的轮廓,最多把模块要设计几个、流程的实现过程想个差不多就开始写了,而本单元在指导书中明确要求先做好建模,然后开始代码实现,这种要求还是超过了之前我的实践。

我个人总体遵从了指导书的要求,但是奈何抽象思考能力实在有限,我的生物硬件似乎比较一般,仿真能力不足以支持我完全设计完所有细节,然后按照我设计的UML直接实现,或者说,我认为这样效率仍然不够高:我更倾向于比前三个单元多走几步,也就是把类的数量、数据(数据类型)、核心方法设计好,然后开始实现,如果实现中出现问题(这种问题大概率也不是我能在设计的时候考虑到的,毕竟设计考虑的是分模块、功能,写代码是要通过过程进行具体实现),那么就再折回去修改我的UML,进行一定的微调。

也就是说,本单元我的正向建模开发的实践路径为:建模90%——>尝试实践——>根据实际情况微调UML设计,并完成实现。下面以几次作业的类图、状态图、顺序图的实现过程来详细阐述。

类图

我认为类图是最基本也是最贴近架构设计的一类UML图之一。第一次作业只要求了类图,大概就是因为类图对于初始框架的搭建最有约束作用,虽然评测的时候留了不小的冗余空间,但是仍然起到了一定的约束作用。

从第一次作业的指导书和以往的迭代特性就不难看出,提到的几个能放书的地点一定是要设计出一个类来进行的,即使是看似没用的自助查询机也有增加更多功能的可能性——虽然最后基本没怎么拓展。OO这一学期下来教会我很重要的一件事就是,OO这种工程或者项目的开发,和程设或者数据结构那种做题不一样,后者是写完一次就不会再用了,因此想怎么tricky就怎么tricky,但是对于要迭代、修改的,大到一定规模的项目,一时的偷懒可能导致后期理解困难、迭代成本高,甚至需要直接重构。

因此,为了方便,在第一次作业,我就直接设计了以下类(为了更具有表意性,这里的类名和程序中不一样):

  • Library类,作为图书馆整体,除了Person类可以说都在这里
  • AppointOffice、BorrowOffice、BookShelf 三个“书籍所在地”(后来增加了DriftCorner漂流角)
  • SelfService、Arranger两个介于Library和三个书籍所在地这两个层次之间的类,其中Arranger负责开馆闭馆前的整理,以及一些过期、预约等信息的记录。
  • Book和BookCollection类,前者意义自不必说,后者是AO、BRO、BS三个类的父类,本意是为一些统一的操作减少代码量(虽然其实最后发现,不同的存书的地方有不一样的remove方法,很多方法都需要重写以增加或自定义一些操作,难以直接使用父类的,可能接口是一个更好的选择

状态图

第二次作业要求我们绘制书的状态图,这确实是整个系统各项功能的主流主线。很明显,根据书籍的地点,就能写出几个状态:在书架、在借还处、在预约处、漂流角、被借走。显然书籍是没有终止状态的(没有出现书籍回收、损坏的需求),所以是一直循环的。然后在设计类图的时候其实就大概想清楚了如何转移,所以写好以后再根据实际情况填好Trigger即可(虽然我觉得是需要Guard的,但是好像最后发现只要有一个即可,我觉得Guard太难写了,于是就偷了懒,实际不应该这样)

顺便一提,看到需要画状态图的时候,我本以为需要记录书的状态,然后去对应(之前我的实现预留了这个功能,但是实施的时候因为发现没什么用状态都是瞎写的),后来发现完全是我们自己去在代码里面标注(甚至是你随便标注,只要能对得上就OK),于是身边有人投机取巧,写了一个从initialState到finalState的一条转移就直接AC了。

顺序图

顺序图能很好地描述一个过程(可能是一个功能,或者一个方法)的各类之间的协作流程。指导书要求在我理解看来是一个用户从order到get的过程,但是由于在考期,加上这次评测非常简单,所以很多人选择了直接画两条线,中间的流程就忽略了。我也差不多,OS实在是太难熬了,在这里真诚地表示歉意。

单元架构设计

架构的设计思路其实在上一节的“类图“部分有一定简单的介绍,这里就补充一点:

对于一个指令,Library负责解析指令,拆解为各个步骤,调用该调用的函数,是“壳”;真正执行底层指令的是下面两层,而实际上最“辛苦”的是Arranger,涉及多个藏书点的移动、各种信息的修改(尤其是最后一次作业,看似就是加了个Credit,但是要额外记录很多东西来支持),还需要考虑顺序和一些日期等细节问题。

最后一次作业的架构如下(展示了UML类图,与上交版本有所区别,主要是重排了顺序,让层次更加清晰):

在这里插入图片描述

架构设计思维的演进 从U1到U4

其实这一点在上面也说到了,我认为最主要的区别是:从一开始从只想本次作业如何实现、如何最方便的实现,到如何设计一个富有弹性的、可扩展性充足的、安全的架构,不论是SOLID原则,还是JML规约化或UML这种先严格设计再实现的方式,都让我对架构设计的认识有了质的飞跃。具体来讲,可以从以下几个问题回答。

1. 架构设计到底是为了什么

曾经我认为,设计架构单纯是为了防止出现写着写着发现不对要推倒重来、或者写着写着忘了怎么想了,因此要产生一个草稿一样的东西,就好像在写作文前,先要列个提纲。

不过这几个单元下来,我发现这个提纲服务的并不仅仅局限于一次的作业,而是为未来拓展/修改等改动留下空间,在OO语境下,就是提前多做一些事情,尽量免去未来的折磨与痛苦。虽然很有可能,迭代方向与预期完全不同,但是这也让我们跳出指导书的要求,去从需求者或者说甲方的角度思考了问题:这个地方未来可能能够增加一些功能,我现在实现的时候就预留一些空间。现在写复杂几行,未来可能就能少改几行。

另外,反复打磨一个架构,拥有了一套合理的架构以后,不仅写的时候心理有底,调试的时候也能快速定位问题。

2. 架构设计要做到什么程度

最开始我的架构设计,无异于做数学题的草稿纸,写完这道题大概也就没用了,顶多是考试有时候要检查的时候,会拿出来再看一眼。但是现在我认为,架构作为一个参考,如果设计了一个很好的架构,实际上他的用处可能比指导书还要大。写代码的时候要参考、调试的时候对着架构也能大概知道哪更有可能有问题,下次迭代的时候也要按照上一次的架构往里新增/修改东西。

因此,架构虽然不是面面俱到,但是至少能够让我在各种使用场景下都能看的清楚,用着合适。

3. 架构设计怎么做

这一点我认为相对来说我的理解可能没有那么丰富,毕竟一共就四次”从头设计“的机会,而且也不是每个单元都能抓住这个机会,第一单元不懂怎么做,第二单元后面遇到了冯如杯,第三单元JML实际上架构已经80%定死了,第四单元才又有机会斟酌损益(第三次还赶上OS期末),所以个人认为我对于架构设计的实践可能仍然处于试错阶段

我可以谈一谈失败的经验,也就是我设计出坏的架构是怎么样的:

一个类太臃肿

可能本来就是好几个类协作完成的功能,一开始拆了觉得每个类就一点点,于是就非得放到一起,再新增的话要么重构(分拆)、要么就变得更加臃肿。

过于追求代码复用和”简(tou1)洁(lan3)“

在第一单元表达式里,我清晰地记得,要实现去括号功能,但是为了性能分尽量也要化简,于是我做出了一个令我后悔的决定:既然解析的时候有expr,term,factor这么几个层次,我化简到最后应该都是加法,干脆把化简以后的也用term表示。后来我发现:这不仅让我的term要写很多类似但是又服务于两种“term”的方法,而且非常不利于调试和后续迭代!本来第一单元的架构设计难度并不太难,而且training和实验都给出了很大的提示,我这样做硬生生给自己增加了难度。

没有明确每个类的意义,职责分配不明确

这种情况一般发生在没有做好设计就急于开始写的情况。有时候觉得很明显就分这么几个类,随便一写就有了,结果写着写着发现,有的类实际上完全没用,有的类其实还要分开,在写代码的时候如果再返回去大改架构(如果没有重构的话),容易出现很多问题。比如可能会想着尽快修正,没有全局视角的考虑,在修改过程中直接引入了bug或带来了新问题,或者原来的某个类是依赖现在这种设计的,但是现在大改以后另一个类就会出现问题。

这里举一个第一单元第一次作业我的问题,其实非常可惜,但是上来就给我了一个教训。问题很简单,就是我写完了代码,但是听说有一个性能优化就是把正项提前,这样能少一个符号,我本来觉得还挺容易的,结果写的时候发现要小改一下一些函数,毕竟要判断是不是第一个(判断是不是输出正号),之前我写的时候会自动跳过0,所以最终输出的时候绝对是没有0的,但是因为不知道哪里改动了一下,不小心让式子里有了0,但是当时我觉得好像我的输出有0也无所谓,结果0变成了没人管的情况,直接把我强测给挂了。

测试思维的演进

这里说来惭愧,我很多情况下测试都依赖了舍友或其他同学的评测机,以及一些流传的强数据。为数不多自己测试的时候,一般就是捏造一些极端情况,看看时间可不可以接受,看看会不会出RE(电梯那单元我经常RE),有的时候也会在重要的模块写完后,单独测试一下这个和我想象的一不一样。测试这里,大概就是这样。

当然,在第三单元JML规格里,课程组强制我学会了JUnit对规格的测试,这倒确实令我受益良多,甚至一些问题可能我也会出现,比如浅克隆问题等。

课程收获 & 结语

OO面向对象课程,作为为数不多没有理论考试的核心专业课,不可避免地让我们把重心转向了平时作业。事实证明如果再有理论,这门课只有3学分就太令人难以接受了。

在这门课上,我的收获 实践>理论,理论上我也确实接触了很多没听说的东西,可能一些我以后在软工课会学到,一些软件质量或者软件设计的理论啊,JML啊等等。而实践上,最基础的肯定是JAVA会写多了,代码能力直线上升,现在看来OOPre简直是小菜一碟,写一万行a+b problem也不如写一次项目。另外,中测-互测-强测的机制也很大程度上磨练了我的心态和意志,周日早晨起来第一件事是交一发样例看看是不是获得2分,看到被hack 1/xx的那一刻心脏骤然停止跳动的感觉,以及同房代码被我发现bug那一刻的的窃喜…

研讨课也是很创新的方式,感觉BUAA两年间确实这种形式的课程我还没怎么见到(大概是大一没有报新生研讨课导致的),随机分组讨论这种形式很有意思,不仅让我们能有一个和自己可能并不熟悉的人面对面交流自己的代码、架构的机会(就像公司里面做code review或者开会一样),也让我交到了朋友。

最后,感谢课程组的每一位老师、助教和搭建评测机供大家测试的同学,以及和我探讨、为我答疑解惑、一起debug的舍友们,没有你们的支持,OO这座大山不是我一个人能够跨越的。

2024.6.13

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值