• OO之旅——UML完结篇


    OO第四单元总结

    (1)总结本单元两次作业的架构设计

    (2)总结自己在四个单元中架构设计及OO方法理解的演进

    (3)总结自己在四个单元中测试理解与实践的演进

    (4)总结自己的课程收获

    (5)立足于自己的体会给课程提三个具体改进建议

    1. 本单元作业架构设计

    1.1 UML1设计

    ​ 本次作业主要是实现一个UML类图解析器,可以通过输入各种指令来进行类图有关信息的查询,有关mdj文件的解析包仍有官方提供,我们只需要对解析出来的UML各个构成要素进行存储与联系即可。

    分析

    对于一个一个UmlElement,其各个属性含义如下:

    • UMLElement
      • visibility:该元素的可见性
      • name:该元素的名字
      • _type:该元素的类型
      • _id:该元素的id

    本次主要包含五种UmlElement元素,如下。

    • UMLClass

      • _parent:对应的是一个特定的值
        -UMLAttribute
      • _parent:对应该属性所在的类
    • UMLOperation

      • _parent:对应的是该操作所在方法的id
    • UMLParameter

      • type:该参数的数据类型
      • direction:方向,in-参数;return-返回值
    • UMLAssociationEnd

      • reference:该关联端对应的类或接口的ID
      • multiplicity:重复度,该关联端对应类或接口的实例个数
      • aggregation:聚合,暂未使用
    • UMLInterface

      • _parent:同UMLClass,对应的同一个特定的值

    其中,umlClass与umlInterface可直接解析;attribute,operation,parameter 以及associationEnd 由于分别对应了相应的 类,类,操作,类或接口,故有可能会出现在相应parent的前面,造成空指针错误,因而需要在录入时,分多次遍历。

    此外,本次作业还包含了三种元素之间的关系

    • UMLInterfaceRealization
      • _parent:同source
      • source:对应实现该接口的类的ID
      • target:对应该接口的ID
    • UMLGeneralization
      • _parent:同source,对应子类ID
      • source:对应子类ID
      • target:对应父类ID
    • UMLAssociation
      • end1:对应其中一个UMLAssociationEnd的Id
      • end2:对应另外一个UMLAssociationEnd的Id

    至此,所有元素以及元素之间的关系已经一目了然,我们只需保存相应的元素即可,而关系本身则不需要保存。

    架构设计

    ​ 本次作业主要包含两个包:utilselementsutils 实现了官方要求的MyUmlInteraction类 。 elements 中均是为了更高的扩展性,基于官方包中的各个UmlElement 元素重写的各个 MyUmlElement方法。文件树如下:

    └─homework
        └─uml1
            │  Main.java
            │  
            ├─elements
            │  │  MyUmlElement.java
            │  │  
            │  ├─classifier
            │  │      Answer.java
            │  │      MyUmlClass.java
            │  │      MyUmlClassifier.java
            │  │      MyUmlInterface.java
            │  │      
            │  └─meta
            │          MyUmlAssociationEnd.java
            │          MyUmlAttribute.java
            │          MyUmlOperation.java
            │          MyUmlParameter.java
            │          
            └─utils
                    MyUmlInteraction.java
                    
    

    类设计

    homework.uml1.utils
    • MyUmlInteraction
      • 存储了UmlClassUmlInteraceUmlOperation以及UmlAssociationEnd
    homework.uml1.elements
    • MyUmlElement
      • 继承了UmlElement
      • 重写了equals()以及hashcode()方法
    • meta.*
      • 实现了各个MyUmlElement
      • 其中,MyUmlOperation需要保存其相应的MyUmlParameter
    • classifier.*
      • 由于类与接口都需要存储相应的属性,方法,关联对端,故将其抽象为MyUmlClassifier
      • Answer类存储各类查询指令的答案,保证重复指令只查询一次,节省CPU时间与程序运行时间。

    其类图如下:

    1.2 UML2

    本次作业在上次作业的基础上,进一步实现了对UML顺序图和UML状态图的解析,以及增加了三个规则验证

    分析

    UMLClassModel

    同上次UmlInteraction。

    UmlCollaboration

    collaboration下可以有多个时序图交互

    • 第一层次
      • UmlAttribute(协作对象)
        • _parent:所在的collaboration 我猜(与UmlInteraction相同)
        • type:"" 我懵逼了
          扮演Role1,Role2, ... 角色
      • UmlInteraction(交互模型)
        • _parent:所在的collaboration 我猜
    • 第二层次
      • UmlMessage
        • messageSort:消息类型(同步,简单等)
        • _parent:所在的interaction
        • source:消息发出者(lifeline)
        • target:消息接受者(lifeline)
      • UmlLifeline
        • _parent:所在的interaction
        • isMultiInstance:不懂
        • represent:所代表的Role(UmlAttribute)
      • UmlCombineFragment
    UmlStateChart
    • 顶层
      • UmlStateMachine
        • _parent:上一级
    • 第一层
      • UmlRegion
        • _parent:所在的状态机(UmlStateMachine)
    • 第二层
      • UMLPseudostate(初始状态)
        • _parent:所在Region
      • UmlState
        • _parent:所在Region
      • UMLFinalState
        • _parent:所在Region
    • 第三层
      • UmlTransition
        • _parent:所在Region
        • guard:表示该状态转换所能发生的时机(01阈值)
        • source:源状态
        • target:目的状态
      • UMLEvent
        • _parent:对应所在的transition
      • UmlOpaqueBehavior

    架构设计

    ​ 本次设计延用了上次的架构,仍然主要包含两个包:utilselementselements 补充了state以及interact等包,分别用于对状态图与顺序图的解析,文件树 如下:

    └─homework
        └─uml2
            │  Main.java
            │
            ├─elements
            │  │  MyUmlElement.java
            │  │
            │  ├─classifier
            │  │      Answer.java
            │  │      MyUmlClass.java
            │  │      MyUmlClassifier.java
            │  │      MyUmlInterface.java
            │  │
            │  ├─interact
            │  │      MyUmlInteraction.java
            │  │      MyUmlStateMachine.java
            │  │
            │  ├─meta
            │  │      MyUmlAssociationEnd.java
            │  │      MyUmlAttribute.java
            │  │      MyUmlLifeline.java
            │  │      MyUmlMessage.java
            │  │      MyUmlOperation.java
            │  │      MyUmlParameter.java
            │  │      MyUmlTransition.java
            │  │
            │  └─state
            │          MyUmlFinalState.java
            │          MyUmlPseudostate.java
            │          MyUmlState.java
            │
            └─utils
                    MyUmlClassModelInteraction.java
                    MyUmlCollaborationInteraction.java
                    MyUmlGeneralInteraction.java
                    MyUmlStandardPreCheck.java
                    MyUmlStateChartInteraction.java
                    
    

    类设计

    ​ 本次基于上次作业的架构重点实现了三个交互类,一个检查类,并将其耦合在一个MyUmlGeneralInteraction 中;同时基于需要,添加了几个相关的UmlElement类,并适当修改了MyUmlClass 以及MyUmlInterface类。

    其类图如下:

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

    第一单元 - 多项式求导

    ​ 一开始,刚刚接触OO的我对于老师与助教不断强调的"架构"的理解懵懵懂懂,而程序设计与分析则更是停留在C语言面向过程的编程思维中。本单元作为一个供我们快速入门OO的基础篇,对于作业需求的逐层递进,我认为非常合理且十分有必要的,迫使我们在变幻莫测的需求与测试的夹缝之中,一步步朝着面向对象思维的转变。尤其是在第三次作业时新增的对嵌套函数的求导,可以说如果没有一个好的架构,是支撑不起这样复杂的Wrong Format的判断以及递归嵌套的求解的。

    ​ 在本单元的作业中,我在第三次作业中第一次尝试使用了抽象类,这样做的好处是对于每个项的求导,我不再需要一个巨大的switch来判断其到底是一类,从而进行强制类型转换后再调用相应的求导方法;取而代之的是,对每个abstract类的元素,对其方法的调用,会自动向下转型为对相应子类方法的调用。

    第二单元——多电梯调度

    ​ 本单元第一次接触多线程,一路走来可以说是十分享(tong)受(ku)了,但多亏了老师在PPT中给我们提供的两种架构设计思路,减轻了我们的负担,同时也更好地帮助我们体会架构的重要性与优越性。

    ​ 本单元最终架构还算说得过去,我对电梯的解析主要采用了 Producer-ConsumerGuarded Suspension 模式。主要有请求输入装置、主控制器以及电梯三个线程,难点在于对换乘请求的处理。但对于高内聚,低耦合的把控却不够,尤其是对C电梯的处理。由于迷之3楼只有C电梯能够上去,出于时间紧迫,我最终选择了枚举的方式,考虑了各种可能的情况,如"低楼层->3"、"高楼层->3"、"2->3"、"4->3"等等,大段的if-else嵌套,即便有充分的注释,回头再看时理解与检查起来也十分的费劲

    第三单元——类地铁管理系统

    ​ 老实说,本单元最后一次作业一开始的架构十分的差劲,出于种种原因,在ddl前一天晚上选择了重构,结果写了一堆*,强测结果可想而知。在后续bug修复过程中,尝试了"1+2"、"1+1"式拆点法以及各种可能的架构,总共重构了四次,每一次重构都有新的体会与感悟,一个优美的架构好处不仅仅在于其可扩展性,由于模块与模块之间耦合度低,逻辑重合度低,其bug调试及定位难度也大幅降低,同时也能更好的借助JUnit进行覆盖性测试。

    第四单元——UML元素解析器

    ​ 作为OO课程的最后一个单元,在阅读与学习了整整一个学期优秀的代码后,加上有充分的时间设计,本单元作业的最终架构还算比较满意,并且对可扩展性、耦合度、鲁棒性都有一定的考量,也算小有所成吧。尤其是从第一次到第二次作业的扩展,仅仅是在原有的基础上添加新的功能,而原有架构与功能也经历了强测的检验,从而在完成作业时也十分得心应手,更是让我体会到了好的架构的方便之处。

    3. 测试理解与实践的演进

    ​ 测试是软件工程中必不可少且可以说是最为重要的一个环节,一个良好的架构是对定点测试与bug修复的前提,对拍器是对大大提升测试效率的工具,而大量随机数据的投喂则是对软件质量的保障。

    ​ 第一单元难点在于对各种WF情况的考虑。在一开始,无论是公测还是互测,我均是在肉眼debug,但主要还是集中在对WF的检测上。在第三次作业中借助python中的numpy模块以及大佬的数据生成器,终于实现了对拍,提高了测试效率,但由此也导致了一个问题:我不愿意再去花大量的时间与精力去琢磨对方的程序逻辑,而把希望全部寄托在大量的随机数据上。

    ​ 第二单元则主要是线程安全以及电梯运行时间的把控。多线程的测试应该是最为头疼的了,虽然官方给提供了标准运行时间的解析包,使得TLE问题较为容易进行调控。但线程运行时的随机性而导致的线程安全问题,却大大增加了bug定位与复现的难度,从而导致难以调试,因而bug定位更多地仍然是从分析整个程序运行的逻辑上入手。

    ​ 第三单元开始接触到 JUnit 单元测试,同时也学习到了基于JML的规格检查以及 JML Unit 工具的使用。此外,输出格式的固定与统一也使得正确性的检验较为容易,直接fc即可,给自动化测试的部署带来了极大的便利。

    ​ 第四单元由于数据是基于StarUML的,因而难以生成大量随机数据进行测试,但其对于 JUnit 单元测试仍旧十分友好,在本单元的测试中,我仍旧重点采用程序逻辑分析法,对每条查询指令以及每一类规则检查都尽可能地进行了覆盖性检查,如下是我对R003的分析:

     /**
         * 检查UML009 规则
         *
         * 重复继承的情况最为复杂,需要检查是否有
         * 0. 类重复继承
         *      由于不支持类的多继承,故类的重复继承体现为循环继承
         * 1. 接口重复继承
         * 2. 类实现了 "重复继承" 的接口
         *      2.1 一个类直接实现了某个 "重复继承" 的接口
         *      2.2 一个类的父类实现了某个 "重复继承" 的接口
         * 3. 类实现了 "重复" 的接口
         *      3.1 一个类直接实现了两个 "重名" 的接口: C1 implement I1, I1
         *      3.2 一个类实现了一个接口和这个接口的父类: C1 implement I1,I2 && I2 extends I1
         *      3.3 一个类与其父类都实现了同一个接口:
         *                      C1 implement I1 && C2 implement I1 && C2 extends C1
         * @throws UmlRule009Exception 规则异常
         */
    

    "测试只是为了尽可能地发现bug,而通过测试并不意味着你的程序就没有bug了"。吴际老师的这句话值得我们去好好反省与深思,我们总是因为过了中测而沾沾自喜,而当强测结果出来时才懊悔不已,痛恨自己如果当初好好做一下测试就好了。可是现实没有如果,假若把这些低级错误,这种侥幸心理放在未来的工作中,很可能迎接你只有一句"YOU ARE FIRED"

    4. 课程收获

    ​ 一学期的OO之旅终于到了终点,这期间踩了不少坑,也吃了不少苦头,但从零基础一路走来,真的能感受到自己的巨大进步。在第一单元,我学会了正则表达式的使用以及OO的基本思想如类,接口,继承等;第二单元则学会了多线程程序的设计,包括线程安全的人为把控,以及SOLID设计原则等;第三单元学习java的建模语言,意识到了设计与实现分离的重要性以及必要性,同时初步体会到了网络流+拆点的精妙之处;最后一个单元的练习更像是一个总结,不仅是对更广义上的面向对象建模设计UML模型的理解,还是对前几个单元所有学习到的知识的一个汇总,整理以及实际运用,都令我受益匪浅。此外,老师及助教也不断强调要严格遵循代码规范,培养良好的编码风格,不能每次都依靠CheckStyle信息去调整,经过一个学期的磨炼,我的codestyle也有了很大的改进与提升。

    ​ 总之,一学期下来,确实能感受到自己己的能力在稳步提升,也逐渐从面向过程的思维中转变了过来,但距离一个优秀的架构师仍有很大距离,我仍然需要大量及深入地通过阅读专业书籍以及模范代码,学习更多更巧妙的设计思想,逐渐形成一套自己的思维体系。

    5. 一点建议

    1. 我认为可以适当减少互测屋的人数,在OO与OS两座大山前,我们很难有足够的精力去把同屋其他7个人的代码都认真研读一遍,更不用说体会其中的精妙,同时找出逻辑bug了。我想,大部分人都会选择部署自动化工具,进行随机测试,如果没有跑出bug,那就随缘...
    2. Bug修复阶段,对于非合并修复驳回的说明过于简略,且过于晚... 希望不要让认真修bug的同学心凉...
    3. 关于Bug修复的接口可以一直保留,便于我们回看并总结自己的bug。
    4. 关于网站,希望可以有侧边的滑动栏... 不然每次都要滚轮滚好久好久...

    最后,在此衷心感谢OO课程组的老师及助教们,你们辛苦了,真心祝愿OO课程越来越好!

  • 相关阅读:
    Android--多线程之Handler
    webkit-transition-
    结构体直接赋值
    shell 俄罗斯方块 杂记
    debian 开启daytime等服务 "xinetd"
    Linux中 etc/init.d “服务"
    <iOS>关于Xcode上的Other linker flags
    多线程总结
    git的使用与分支管理
    -ios项目中安装和使用CocoaPods
  • 原文地址:https://www.cnblogs.com/puzzledAtticus/p/11076553.html
Copyright © 2020-2023  润新知