UML结构与解析——BUAA OO第四单元作业总结

UML与解析架构

UML是什么

统一建模语言(英语:Unified Modeling Language,缩写 UML)是非专利的第三代建模和规约语言。UML是一种开放的方法,用于说明、可视化、构建和编写一个正在开发的、面向对象的、软件密集系统的制品的开放方法。UML展现了一系列最佳工程实践,这些最佳实践在对大规模,复杂系统进行建模方面,特别是在软件架构层次已经被验证有效。(百度百科)

作为一种统一的建模语言,UML具有完全的面向对象特性,UML使用类作为建模的主题对象,能够很好地涵盖面向对象设计中需要的各种关系。除了静态类图,还提供了展示类之间交互逻辑以及类的状态迁移的状态机等图形化表示,通过UML能够准确地描述一个语言无关的逻辑模型,这对于实现前的整体设计是十分重要的。

当然,UML所体现到的作用,就是OO课程中所以一直强调的——架构设计。对于编程者而言,架构设计往往是比实现更为重要的一部分,一个好的架构设计能够使得程序减少出错,并增加可扩展性——总结来说,就是更具有实际使用的价值。

任务描述

  1. Homework13:实现一个UML类图解析器UmlInteraction,针对UML类图中的类、接口、属性、操作、关联、继承、实现等实体进行解析并重构其逻辑关系,进行完成相应的查询请求
  2. Homework14:扩展类图解析器,使得可以支持对UML状态图和顺序图的解析,并可以通过输入相应的指令来进行相关查询。在所解析的UML模型基础上,按照规定的规则检查模型中是否存在违背规则的情况。

任务解读与设计实现

Homework13

类图的解析看似简单,因为官方的接口已经将每一个UML实体信息转化成了字典对。但要求也并不低,因为需要认真考虑各个实体之间可能出现的关联情况,选择合适的数据结构表示这种关联,并在限制条件下完成快速的查询工作。这次作业我针对每一个实体分类重建了类,利用相应的数据结构体现类之间的关系,并且尝试最大程度的利用类构建时的复杂度进行部分计算,使得查询时的复杂度降低。

设计的类图如下:

DGP模块描述

设计中重点有:

  • 使用树形结构处理类之间的继承关系(考虑到类之间只能出现单继承),使用图结构处理接口之间的继承关系(考虑到接口之间的多继承)
  • 将接口(Interface)和类(Class)统一处理,构建了AssociationEnd接口,并设置相应的方法实现,使得Association和generation可以统一化处理,同时为了增加统一性,在接口中增加了addOperationaddAttribute方法,好处是在构架时可以完全统一化处理
  • 使用了单例EndSet统一表示Interface和Class的集合及各自的集合,并使用一下代码实现了类似迭代器的效果
   public void iterClear(ElementType type) {
       this.iterList.clear();
       for (AssociationEnd end : this.idMap.values()) {
           if (end.getType().equals(type)) {
               this.iterList.add(end);
           }
       }
   }
   public AssociationEnd iterNext() {
       return this.iterList.getFirst();
   }
   public void iterRemove(AssociationEnd end) {
       this.iterList.remove(end);
   }
   public boolean iterEmpty() {
       return this.iterList.isEmpty();
   }
  • 在构建树形结构便借用向父节点的递归遍历方法完成继承相关的一切计算,将计算只限制在父-子关系之间进行,对于远亲节点则会自动包含,这就使得最大程度的利用了构建时不可避免的遍历运算。

Homework14

这次作业不仅在类图的基础上增加了状态图和顺序图,而且增加了少量的规则检查。在设计时,我尝试尽可能的将各个图相互分离——我原本想给每个图构建一个类然后进行整合,但后来发现这样的设计会有问题

  • 顺序图和状态图有时会引用到类图中的信息,相互分离则类之间交互会成问题
  • 在整合时,Java中没有合适的机制。首先,类不能实现多继承;其次,我上网查询了通过内部类实现“伪”多继承的方式,感觉这样的话内部类和外部还是会紧耦合,实现上不仅会增加代码量而且没有必要,更会使得一个类变得十分臃肿。

基于以上的思考,我选择并不彻底切割图的设计,利用继承关系直接继承上次已经构建完成的类图代码这个技巧已经被我使用了很多次了,屡试不爽。然后在新的类别中处理顺序图和状态图的代码,同时也尽可能的将代码进行分散设计,分散到各个子类别中。最终形成的设计图为:

DGP模块描述

从上图中也可以明显看到,左侧整体就是上次的类图解析代码设计,右面两只分别是状态图和顺序图的逻辑。继续仿照之前的设计逻辑,将每一个UML实例都创建相应的新类进行逻辑与数据结构的封装。

这次作业中最难的部分我认为在于三条规则的检查,因为规则的检查是要在类图中完成的,因此我将这个逻辑封装进了类图的构建过程中,而出现的问题是之前的设计会自动忽略这些错误,或者说做了容错处理,因此这次进行修改就是需要保留相应的问题并能够提供问题的检查机制。这就需要对原始的数据结构进行更改。其中,最大的问题就是可能出现的循环继承使得原始的树结构有机会变成图结构,这就需要很小心地处理递归出口的条件,并正确判断究竟哪些是环里面的,哪些只是和环中的类有继承关系

更多的一方面问题体现在对顺序图和状态图的理解。完成过程中,因为对两种图的理解不深刻,有很多没有预料到的问题出现,而是在测试时才恍然大悟 “原来UML图还可以这么画” ,这样的感觉是有一些崩溃的,主要还是在设计过程没有考虑全面就急于动手。

四个单元中架构设计及OO方法理解的演进

架构设计

纵向对比来看,架构设计变化体现其实是最明显的。

类数目的变化 从每次的类图来看,最大的不同应该就是每次作业的类都在增加,这倒不是因为复杂度的增加,其实从电梯开始之后的问题复杂度严格来讲并没有增加,但为什么我使用的类的数目在增加呢?最主要的原因就是我尝试着最大可能简化类本身的逻辑。从某种程度上来讲,这是符合设计模式的,简化独立模块的设计的时候就能降低出错的几率,这样当进行模块之间的组合时也就能降低出bug的几率;同时,简单的模块实现起来难度也低。

类之间责任划分清晰 第一次作业时我并不理解面向对象的意义何在,但随着课程的深入,我认识到对象这个数据与数据操作的集合实际上就是赋予了数据一定的“生命力”,即数据不仅具有本身的表示,更有操作能力。因此每一个对象的设计应该是一个独立作用的实体。所以从设计过程中就应该最大程度的解耦合,即使得每个类能够独立运行。尤其是在JML单元的作业中,有些实现方法直接使用强制转化将接口转化为自己的类以使用增加的操作,我认为这是不符合接口的使用规范的,既然设置了接口就应该使得程序能够适应于每一个实现了接口的类。我在后面的程序设计中也是遵循这样的原则的。

架构设计的时间增加 随着作业的进行,我意识到进行必要的架构设计是能够节省编码的时间的,因此每次作业我都尝试使用足够的时间进行设计,明确类的设计及预期的功能,明确相应的算法设计

对OO的理解

这一点我想谈的和结构设计部分是相对应的,所谓OO的特点,我认为最大的就是类本身是一个数据和数据操作的集合,这就使得类本身相比较面向过程而言具有了极大的独立性。一方面,这种独立性是有利于设计模式的展开的;另一方面,这种独立性通过降低耦合较少错误。

处理类之外,OO方法具有的其他特征也赋予了面向对象具有很大的易用性。

  • 继承 继承的使用使得类之间具有层次关系,这使得类不仅具有多样性,也具有某种程度的统一性。这就使得不同部分使用同一个对象的不同层次信息是可能的,对于不需要的信息则可以进行隐藏以保护信息。
  • 接口与实现 这使得类之间能够产生层次关系之外的关联性,即使得不同的类具有了统一操作的可能性,在实现中,这是具有很高的便利性的;接口也使得程序有极高的扩展性,即新的类只要实现的相应的接口就能被调用者直接调用而不需要修改调用者代码。

四个单元中测试理解与实践的演进

整体来看四个单元的作业,我既使用了黑箱测试的方法——自己尝试着写自动生成数据的代码并自动运行测试;也尝试使用了Junit进行自动化测试,但最终的自动化测试方法效果并不是很明显。

在我来看,使用Junit的好处应该是在持续多次拓展开发中有很强的实际意义,即每次尽心拓展都能保证不破坏程序原有的功能;但对于单次开发测试而言,从构建场景到设计数据,都是很繁琐的工作,虽然能够很有效的保证代码实现符合设计,但这也是依赖于对设计本身是否和需求一致的考研。从这个角度来看,使用Junit测试还是依赖于设计的,那么久回到了最初的问题——设计本身必须是合理且有效的。

课程收获

从OO的课程中,我不仅收获到了面向对象程序的构建方法,更多的是对面向对象程序设计模式的理解。但这种设计的思维实际上不仅仅限于面向对象的方式。其实,面向对象只是提供了解决问题的一种思维方式,但其实立足于需求更全面多层次多角度的考虑问题应当是解决问题最本质的思路。

就计算机技能本身而言,其实这是我第一次体会到面对一个需求,需要转化成设计,需要根据设计实现代码,需要针对实现做出符合需求的测试使得代码符合要求。这个过程中,不仅需要考虑正确性,甚至需要使用合适的数据结构和算法来保证程序运行的效率问题。这是我在之前的课程中不曾有过的收获与锻炼。另一个角度,OO这种每周Project的课程模式不仅给了我压力,也让我认识到自己实际上有能力应对这样的课程模式,从某种程度上对我自己也是一种锻炼。

改进建议

  1. 增加对测试的要求。我们的课程中对测试的要求实际上是比较小的,有时候只保证的程序的正确性但忽略了运行效率,但这些问题实际上都是可以在测试过程中发现的。然而,实际授课中并没有涉及到测试过程的详细讲述,更没有系统性的关于测试的训练。测试本身应该是比较容易进行有效性衡量的,所以我认为可以单独给出一些时间进行程序测试,看同学们给出的测试方法能否对需求完全覆盖。
  2. 上机课程安排不合理。上机课程中按照老师的意思是对课上内容的理解与消化,但实际上上机题目会很不稳定而且并没能起到很好地作用。有时题量过大无法完成,有时又会很简单很快就能完成,大多数时候也感受不到和理论课有紧密的结合。所以我希望课程组能够改变一下上机课程,使之更有效。
  3. 课程通知机制不完善。进行计组课程时,所有的课程通知都会通过微信发送,这是有效的;相比较而言OO课程通知使用网站不定期更新的方式其实有些不负责任——很多时候悄悄更新了代码或者要求,不经常查看课程网站便无法注意到(要么确定一个更新时间也是可接受的方式)。而且有时候DDL会出现临时更改的现象,甚至严重的会从三天缩短到不到12小时,这是比较难以接受的。相比之下,指导书或者中测、强测的时间点经常会滞后。我认为既然课程组要求我们严格遵守DDL,就应该从自身做起严格遵守DDL。
posted @ 2019-06-20 21:12  Flymin  阅读(301)  评论(1编辑  收藏  举报