BUAA_OO_Unit4 总结

BUAA_OO_Unit4 总结

一、UML解析器架构分析

(一)需求分析

本单元要实现一个UML解析器。具体来说,本单元作业需要继承官方接口UserApi,在自己实现的MyUserApi类中实现对应方法。更进一步,本单元作业要在MyUserApi类的构造方法中实现对UML图形的层次化建模(包含类图、顺序图和状态图三个部分);之后通过官方提供的对应UML元素类实现自己的UML元素类,在各个元素类中完成官方接口定义的查询方法;在最后要实现模型有效性检查类,从而在层次化建模完成后对整体UML图模型进行检查,排除不合法的模型。整体设计架构如下图所示。

(二)整体架构

 

 

 

(三)架构分析

1 层次化建模

由于UML模型图的输入是乱序的,为了保证层次化建模,需要对输入的UML模型进行多轮遍历,具体如下:

Round 1:

  • 类图:UmlClass, UmlInterface;

  • 顺序图:UmlInteraction;

  • 状态图:UmlStateMachine;

Round 2:

  • 类图:UmlAttribute(同属顺序图), UmlOperation, UmlGeneralization, UmlInterfaceRealization, UmlAssociationEnd;

  • 顺序图:UmlLifeline, UmlEndpoint;

  • 状态图:UmlRegion;

Round 3:

  • 类图:UmlParameter, UmlAssociation;

  • 顺序图:UmlMessage;

  • 状态图:UmlState, UmlPseudostate, UmlFinalState;

Round 4:

  • 状态图:UmlTransition;

Round 5:

  • 状态图:UmlEvent

综合层次化要求和代码复杂性后,具体的层次设计主要采取两层结构,包含:

  • MyClassMyOperation

  • MyInterface

  • MyInteractionMyLifeline

  • MyRegionMyStateMyStateMachine只充当MyRegion类的外壳的作用,严格意义上不是一个层次)。

具体的层次化建模封装到了parse*方法中。

2 查询方法设计步骤

事实上,在一个合理的层次化建模总体模型下(特别考虑到本单元作业对性能几乎没有要求,所以可以使用最简单的代码逻辑),实现每一个接口待实现的查询方法只是时间问题。总体来说包含如下步骤:

  1. 抛出存在性异常。

    在我们建立的层次化模型中,同名同姓的类图/顺序图/状态图元素是完全可能存在的,此时需要抛出所谓的存在性异常(包含不存在异常+存在多个异常)。由于基本上所有方法都需要进行存在性异常的检查,所以将这个过程封装为一个方法,即MyUserApi类中的throw*Exception方法;

  2. 提供查询方法接口。

    MyUserApi类中提供方法接口,调用具体类中的查询方法;

  3. 递归下降实现查询方法。

3 模型有效性检查

在本单元的最后一次作业中,我们需要实现模型的有效性检查。实现过程其实和具体的查询方法类似,但是考虑到代码风格的需求,本次作业将有效性检查封装成了一个具体的MyUmlStandardPreCheck类,并在类中实现相应的静态方法;通过在MyUserApi中传入具体的模型参数并向MyUserApi提供调用静态方法的接口,实现两个类的交互。

(四)架构改进想法

在绘制本单元作业的架构图,特别是其中MyUserApi部分时,我曾一度想放弃罗列所有的方法,因为类中方法太多了。但是仔细想想,完整的绘制或许可以提醒我一个基本事实:不合理的架构设计会导致代码臃肿。事实上,本单元的架构完全可以有进一步的改进,主要包含以下方面:

  1. 建模与查询相分离。本单元作业的建模和查询事实上是基本独立开的,完全可以另外实现一个建模类进行层次化建模,实现建模方法的封装;

  2. 设计类的组合、继承关系。在本次作业中,我主要使用组合+方法接口的方式实现具体查询方法,这就要求在MyUserApi类中要提供具体的方法接口,还是会导致方法过多的问题。事实上,官方接口已经对此做出提示:可以划分类图/顺序图/状态图的不同查询需求,通过继承关系统一联系到MyUserApi中。

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

(一)层次化建模

在第一单元中,我们通过表达式解析任务初步了解OO的基础——类的封装性。具体来说,我们通过对每个表达式层次对象进行封装,设计具体的成员属性和成员方法,最终实现了表达式解析任务。

再回过头来看我的代码结构,不能说不好,但是也不能说完全符合面向对象要求,例如实现接口还是设计类的问题。但是在设计过程中,我确实尝试采用荣文戈老师所说:“假设你有很多程序员,将任务分出去”的想法,这样的想法带给我的好处就是任务分得清,降低了整体代码的耦合度,这确实降低了我debug的难度。

(二)多线程设计

在第二单元中,我们通过多线程电梯调度的任务初步了解了Java多线程的知识。具体来说,我们通过继承Thread类来实现线程,通过synchronized关键字保证线程的互斥,并且通过wait-notify方法对实现线程的同步。

在整体架构的设计上,本单元我们接触了生产者-消费者模型,实现了基本的线程间信息的传递;在架构设计上我还采用了组合等类的联系,使得整体架构更加便于管理。

(三)规格化设计

在第三单元中,我们通过社交图网络的任务初步了解了JML规格设计的知识。具体来说,我们通过官方提供的方法接口及其JML描述,实现自己的类与方法,总体来说是一个阅读契约-理解契约-实现契约的整体过程,这也在OO方法上给了我更高层次的提示——设计并不仅仅是类之间功能的协作,更是需求同实现的协作,这里的协作是需要严格规约的。

在架构设计上,本单元的作业我尽力将架构封装为实际结构层-抽象结构层-算法层三个层次,并在设计过程中尽力实现软件设计原则(如开闭原则、单一职责原则、里氏替换原则)等,使得架构设计思想向更抽象的层次进一步。

(四)面向对象基础——UML

最后一单元,我们通过UML解析器的实现回顾面向对象最重要描述语言——UML的相关知识。总的来说,这和第一单元涉及的层次化设计思想是类似的,但不同于第一单元的是,我已经可以灵活掌握继承、组合、关联等类的不同关系,从而实现更加合理的架构。这也许就是架构思维提升的体现。

三、测试理解与实践演进

测试是OO课程中一个重要的环节。在四个单元OO的学习过程中,我实现了不测试—测试调试—手动构造数据的演进,一步步加深了对程序正确性的保证。

第一单元与第二单元的第一次作业中,我基本上采取鸵鸟策略,由于本身对Java语言就不是很熟悉,完成作业已经使我有些心力交猝,所以就不测试,逃避测试,等待强测与互测错了再修bug。直到第二单元的第一次作业,一个小小的for循环迭代的细节让我挂了几乎八成的测试点,我意识到鸵鸟策略或许是不应该的。所以第二步,我开始漫长的合作测试道路(但是其实是单方面的合作,即使用他人的评测机跑自己的程序,然后根据错误调试)。尽管测试的自主性有待质疑,但是不得不说,我在一步步debug的过程中逐渐锻炼了自己定位错误与修正的能力,程序的正确性有了较高的保障。但是老话说得好,打铁还需自身硬,在第三单元中,我面向性能测试尝试进行手动构造数据,并且在三次作业的互测中都进行了成功hack。这其中的喜悦确实是不一般的。

但是我也深知,对于自动化测试我还基本未涉足,正好暑假有Python课程,看看能不能在语言学习后尝试自主构造测评机,以弥补测试中这巨大的遗憾。

四、课程收获

总的来说,OO给我的收获是很多的,包括但不限于以下内容:

  1. Java基础语法;

  2. 多线程编程知识;

  3. JML规格化语言的阅读和撰写;

  4. UML类图、顺序图和状态图的相关知识;

  5. 软件设计六大基本原则;

  6. 单例模式、策略模式等设计模式;

  7. 层次化分析等架构设计思想;

  8. 若干不眠之夜与对身体健康的反思(bushi

  9. ...

回首整门课,可以说是在暂时上的课中对我代码能力提升作用最大的一节课,希望我能够将这些内容好好消化吸收,用到未来自己的其他科目学习和工作当中。

五、课程改进建议

  1. 预习课程可以适当分配课时(例如两周,可以把JML和UML的课时适当压缩),同时设置一定分值,push同学去完成预习课程,从而更好进行正课学习;

  2. 可以在指导书中多一些设计模式的提示;

  3. 实验课课前给一些提示或者相关知识的学习,并且在课后开放答案反馈和修正机会。

posted @ 2022-06-29 14:08  LeVoyageur  阅读(38)  评论(0编辑  收藏  举报