BUAA_OO_UNIT4_单元总结&课程总结

一、本单元作业的架构设计

本单元的任务是实现一个UML图的解析器,可以通过输入各种指令来进行对类图、顺序图、状态图有关信息的查询,并在最后一次作业中支持对模型的有效性检查。

第十三次作业

  • 本次作业只需要实现对UML类图的解析,设计总体上与StarUML中的层次结构相同。由于官方包提供的各种UmlElement仅包含name、id、parentId等最基本的信息,不包含各类元素之间的继承、实现、关联等关系,因此我构造了MyUmlClass、MyUmlInterface、MyUmlOperation类对相应的官方类进行封装。

    • MyUmlOperation类的属性如下:

      private UmlOperation umlOperation;
      private List<UmlParameter> inParam = new ArrayList<>();
      private UmlParameter returnParam = null;
      

      umlOperation是原始输入的未经包装的UML方法,inParam是该方法的传入参数列表,returnParam是该方法的返回参数,默认为null。

      在MyUmlOperation类中,除了提供getId、getName、getParentId、getVisibility等官方类支持的基本查询操作,还需要提供addParam、getInParam、getReturnParam等方法用于对参数的增加和查询。

    • MyUmlClass类的属性如下:

      private UmlClass umlClass;
      private MyUmlClass superClass = null;
      private List<MyUmlOperation> operations = new ArrayList<>();
      private HashMap<String, List<MyUmlOperation>> name2Opera = new HashMap<>();
      private List<UmlAttribute> attributes = new ArrayList<>();
      private HashMap<String, List<UmlAttribute>> name2Attr = new HashMap<>();
      private List<MyUmlClass> assClasses = new ArrayList<>();
      private List<MyUmlInterface> assInterfaces = new ArrayList<>();
      private List<MyUmlInterface> realInterfaces = new ArrayList<>();
      

      umlClass是原始输入的未经包装的UML类,superClass是该类的父类,默认为null,operations、attributes、assClasses、assInterfaces、realInterfaces分别是该类包含的方法、属性、关联的类、关联的接口、实现的接口,name2Opera、name2Attr是为了方便后续查询操作所定义的辅助容器。

      额外提供setSuperClass、addOperation、addAttribute、addAssClass、addAssInter、addRealInter以及相应的查询方法。

    • MyUmlInterface类的属性如下:

      private UmlInterface umlInterface;
      private List<MyUmlInterface> superInterface = new ArrayList<>();
      

      umlInterface是原始输入的未经包装的UML接口,superInterface是该接口继承的父类接口列表。

      额外提供addSuperInterface、getSuperInterface方法用于父类接口的添加和查询。

  • 在MyUmlInteraction类中,定义一系列HashMap用来存储各类输入元素,如id2Class、id2Interface、id2Operation等。在构造函数中,首先通过eleClassfy方法对输入的各种UmlElement进行分类,将其包装好后添加到相应的map中。接着依次调用parseGeneralizationparseInterRealizationparseAssociationparseAttributeparseParameterparseOperation方法对不同元素之间的相关关系进行解析。

    public MyUmlInteraction(UmlElement... elements) {
    	for (UmlElement e : elements) {
            eleClassify(e);
    	}
        parseGeneralization();
        parseInterRealization();
        parseAssociation();
        parseAttribute();
        parseParameter();
        parseOperation();
    }
    

    如在parseGeneralization方法中,我们需要实现的就是根据UmlGeneralization的source、target找到对应的类或接口,为其添加继承关系:

    public void parseGeneralization() {
        Iterator<Map.Entry<String, UmlGeneralization>> entryIt = id2Gen.entrySet().iterator();
        while (entryIt.hasNext()) {
            UmlGeneralization currGen = entryIt.next().getValue();
            String subId = currGen.getSource();
            String superId = currGen.getTarget();
            if (id2Class.containsKey(subId)) {
                MyUmlClass subClass = id2Class.get(subId);
                MyUmlClass superClass = id2Class.get(superId);
                subClass.setSuperClass(superClass);
            } else if (id2Interface.contains(subId) {
                MyUmlInterface subInter = id2Interface.get(subId);
                MyUmlInterface superInter = id2Interface.get(superId);
                subInter.addSuperInterface(superInter);
            }
        }
    }
    

    在解析完不同元素之间的继承、实现、关联、包含等关系后,就可以调用相应的方法进行信息查询。对于需要考虑继承关系的查询指令,均采用类似以下的结构进行处理:

    MyUmlClass currClass = findClass(className);
    while (currClass != null) {
        //具体的判断及操作
        currClass = currClass.getSuperClass();
    }
    
  • UML类图

第十四次作业

  • 本次作业增加了对UML顺序图和状态图的解析,实现思路与UML类图基本一致。

  • 由于三种图所包含的元素类型互不相同(除UmlAttribute),查询指令也仅限于对某一种图的解析,不涉及三类图之间的一致性检查等,因此定义MyClassModelInteraction、MyCollaborationInteraction、MyStateChartInteraction类,分别实现对于类图、顺序图、状态图的查询交互。

  • 在MyUmlGeneralInteration类中,定义三个List分别用于存储属于类图、顺序图、状态图的各种元素,在Interaction类中完成对相应元素、关系的解析和各种查询方法的具体实现。

    private MyClassModelInteraction classInteraction;
    private MyCollaborationInteraction collaInteraction;
    private MyStateChartInteraction stateInteraction;
    private List<UmlElement> classElements = new ArrayList<>();
    private List<UmlElement> collaElements = new ArrayList<>();
    private List<UmlElement> stateElements = new ArrayList<>();
    

    对于MyUmlGeneralInteration类每个查询方法,调用相应的Interaction类的同名方法即可:

    public int getClassCount() {
        return classInteraction.getClassCount();
    }
    
  • UML类图

第十五次作业

  • 本次作业新增了对模型有效性的检查,整体架构与第十四次作业保持一致,只需完成对8个检查方法的重写即可,具体实现过程我也放在了相应的Interation类中。

  • 由于cpu限制为10s,大部分检查方法我都采用暴力遍历+递归的方式解决,

  • UML类图

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

  • 第一单元

    • 第一单元的任务是构造一个多项式求导器,并通过迭代开发最终实现包含简单幂函数和简单正余弦函数及其嵌套组合函数的导函数的求解。

    • 在完成第一次作业时,由于刚接触OO这门课程,连继承、多态的使用方法都不是很了解,因此整个求导过程带有比较浓重的面向过程色彩,只能处理没有嵌套的简单幂函数,可扩展性较差,无法满足后续作业中的嵌套需求。在第二次作业时我进行了重构,利用多态机制为运算符父类创建了加法类和乘法类两个子类,并在所有类中均实现求导接口derivable(),对整个表达式进行链式求导。在第三次作业中也基本沿用了这一架构。

    • 在上这门课之前,我一直以为所谓的”面向对象“/”面向过程“就是不同编程语言的语言特性,C语言就是面向过程的,python和java就是面向对象的。通过第一单元的学习,我逐渐开始理解老师所说的”OO是一种设计思路,与使用哪种语言编程无关“。

  • 第二单元

    • 第二单元要求我们模拟多线程电梯的运行,需要根据乘客的到达模式以及电梯的状态和类型进行适当的调度,确保在规定时间内将所有乘客运送至目的楼层。

    • 本单元作业我基本上实现了迭代开发,架构的主要思路是为每部电梯配置一个属于自己的控制器,并由一个总的调度器负责将等待对利润中的请求分配给不同的电梯,每部电梯在自己的controller的控制下实现上行、下行、开关门、运客等操作。

    • 通过本单元的学习与实践,我认识到层次化设计有利于降低不同线程之间的耦合度,一定程度上也增强了架构的可扩展性。在我们的设计中,每个线程应当只专注于自己的任务,不去过多考虑其他部分的实现过程。在新的需求到来时,也应该能够做到避免大幅修改之前已经实现的部分,而是通过增加新的层次或细节来实现新的功能。

  • 第三单元

    • 第三单元要求我们基于给出的JML规格实现一个社交关系网络,并可以通过各种指令实现数据的增删改查。

    • 相较于前两个单元对于架构设计的整体性及可扩展性的考察,本单元对于需要实现的类都给出了相应的抽象类或接口,类中需要实现的方法也都给出了JML描述,相当于已经定好了整体框架,我们只需要填充内部方法的具体实现即可。因此,本单元作业的架构设计主要体现在图模型的维护上。

    • 本单元我们的主要任务就是通过实现官方提供的各种接口来实现的自己的类,这种面向接口式的编程正是OO方法的一大体现。

  • 第四单元

    • 第四单元要求我们实现一个UML图的解析器,可以通过输入各种指令来进行对类图、顺序图、状态图有关信息的查询。
    • 架构设计可以参看第一部分。
    • 本单元也是通过类的继承和接口的实现来体现面向对象思想的。在官方代码包中,所有元素类都由UmlElement这一抽象类派生,UmlClass和UmlInterface的共性特征由也抽取到UmlClassOrInterface这一接口中。

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

  • 第一单元

    学期伊始的我还是太年轻,以为过了中测就万事大吉,直到第二次作业的强测给我当头一棒,才真正意识到自行测试的重要性。

    第一单元采用的测试方法主要是根据题目中的表述手动构造测试样例,覆盖尽可能多的情况,一方面要考虑多种类型因子的组合和嵌套,另一方面要进行边界测试和压力测试,看看对于长度很长或者或嵌套层数比较多的表达式,会不会出现爆栈以及TLE的情况。

  • 第二单元

    由于一开始对多线程相关知识不是很了解,仅仅在代码编写方面就花费了大量时间,且多线程电梯的测试具有不稳定性和不可复现性,因此依旧采用纯手动构造极端测试样例的方法,针对某一特殊情况进行大量测试。构造测试样例时,主要考察在不同请求出发楼层、方向差异较大和大量请求同时到达的高并发情况下电梯能否在规定时间内将全部乘客送达相应楼层,以及是否会出现死锁、线程无法结束等情况。

  • 第三单元

    第三单元中测强度过低,因此大量、全面的测试是十分必要的。

    本单元中,课程组推荐使用JUnit进行单元测试。通过课下自学以及同学们在研讨课上的分享,我掌握了JUnit的基本使用方法,对限时测试、异常测试等相关概念也有了一定的了解。

    在使用JUnit进行单元测试时,要想覆盖每个方法的所有条件分支,需要手动编写大量代码,耗时较长。因此在本单元中我第一次搭建了评测机,开始采用自动化测试,通过对拍来验证程序的正确性。对拍时所需的输入指令,可以通过python程序随机生成,也可以直接从文件中读入手动构造的测试样例。前者主要用于判断程序的正确性,后者主要用于判断CPU是否会超时。

    此外,我还进行了白盒测试,通过代码走查的方法检查自己的程序在逻辑上是否存在不完善的地方。

  • 第四单元

    本单元主要是靠手动构造复杂样例和与同学对拍发现程序的bug。

四、课程收获

  • 知识层面

    • 锻炼了使用java语言进行编程的能力
    • 在学习过程中对面向对象思想有了更深的体会
    • 了解了LOOK电梯调度、并查集、堆优化的Dijkstra等算法,巩固了图论相关知识以及dfs、bfs算法在实际中的应用,对各算法的时间复杂度有了更清晰的认识
    • 掌握了正则表达式、多线程同步互斥、JML规格、UML模型图等基础知识
    • 对工厂模式、单例模式、生产者-消费者模式等设计模式有了更深的理解
    • 对黑盒、白盒测试方法有了进一步的了解
  • 增强了心理素质,收获了极限debug临危不乱的从容和推翻重构重头再来的勇气

五、课程改进建议

  1. 在博客周可以适当布置一些预习任务,以减轻在下一单元刚开始时的无措感。
  2. 希望实验课课后可以公布参考答案,以便同学们通过对照发现自己存在的不足或考虑不周到的地方。
  3. 希望能够降低第一单元对于性能的要求,或者像三四单元一样直接提供官方化简接口,让同学们有更多时间去思考架构设计,而不是沉迷于表达式化简。
  4. 希望第四单元的指导书可以进一步完善,细化对于每一条查询指令的具体要求,每周日下午都泡在讨论区翻看自己有没有错过某条补充说明的体验着实不是很美妙

凡是过往,皆为序章,OO这段难忘的旅程到此就告一段落了,衷心感谢一路以来各位老师、助教们的辛苦付出以及各位同学对我的帮助(❁´◡`❁)!

posted @ 2021-06-26 20:08  polaris123  阅读(60)  评论(0)    收藏  举报