zoukankan      html  css  js  c++  java
  • OO第四单元

    一、作业架构设计

    • 第一次作业

    作业需求

    实现一个UML类图解析器,可以通过输入各种指令来进行类图有关信息的查询。

    难点:

    CLASS_ASSO_COUNT,CLASS_TOP_BASE,CLASS_IMPLEMENT_INTERFACE_LIST

    架构设计:

    类图中的元素有明显的层次性,根据层次重写三个类,对应Class,Interface,Operation。

     

    MyClass属性,MyInterface类似。

    public class MyClass extends MyElement implements MyObject {
      
    private HashMap<String, MyOperation> idOpMap = new HashMap<>();              //Operation id->MyOperation private HashMap<String, HashSet<MyOperation>> nameOpMap = new HashMap<>();       //Operation name->MyOperation private HashMap<String, UmlAttribute> idAttriMap = new HashMap<>();            //Attribute id->UmlAttribute private HashMap<String, HashSet<UmlAttribute>> nameAttriMap = new HashMap<>();     //Attribute name->UmlAttribute private MyClass father = null;                                  //Father MyClass private HashSet<MyInterface> interfaceRealizationSet = new HashSet<>();         //Interface MyInterfaceprivate HashSet<MyClass> assEndSet = new HashSet<>();                    //AssociationEnd MyClass }

    MyOperation属性

    public class MyOperation extends MyElement {
        private Visibility visibility;                                  //Visibility
        private HashMap<String, UmlParameter> idParaMap = new HashMap<>();            //parameter(in)
        private UmlParameter returnp = null;                              //parameter(return)
    }

    实现思路:

      1. 由于不保证元素出现的顺序,先遍历元素并分类。相当于给每个uml元素准备一个口袋,按照类别放到各自的口袋。
      2. 遍历Class和Interface对应的口袋,生成对象。这两种对象类似一个容器,通过add方法,添加自己的方法,属性,或者关联。
      3. 再遍历Operation,Attribute,Association,Generalization,Realization,添加到对应容器。
      4. 实现查询指令,这里只探讨刚才提出的三个难点。他们有一个相似的解决思路——dfs。比如CLASS_ASSO_COUNT的计数函数,先调用父类的函数,返回父类Association的集合,再自己的Association合并。CLASS_IMPLEMENT_INTERFACE_LIST指令应该注意,既要有父类实现的接口,又要有自己的接口继承的父接口。
      5. 应用缓存思想:把查询过的结果储存好,接到指令先判断是不是有存储的结果。
    • 第二次作业

    作业需求:

      1. 扩充状态图和顺序图的查询指令。
      2. 对类图进行模型有效性检查。

    难点:

    检查循环继承,检查重复继承;查询一个状态的后继。

    架构设计:

    有四个功能相对独立的接口:

    UmlClassModelInteraction

    UmlCollaborationInteraction

    UmlStateChartInteraction

    UmlStandardPreCheck把方法实现在一个类里面代码会超长,功能也会十分混乱。应该写四个单独的类来实现方法,在主类里复用这些方法。代码复用有两个途径,一是继承,二是在类里创建对象,我选择了后者。

     

    状态图和顺序图采取了和类图相似的层次性架构。我还写了一个有向图的类(Graph),封装求解可达节点的算法。这是状态图的结构。

     

    实现思路:

    1. 循环继承:类是节点,继承关系是有向边,从任意一个类开始dfs,记录经过的路径,把路径传入更深一层的递归。如果新遇到的类已经在路径里出现过,说明在这条路径上,从这个类上一次出现到这一次出现之间的节点形成一个环,把环上的节点加入一个集合。从剩下类中挑出一个不在该集合内的类,继续dfs,直到所有类要么遍历过,要么出现在集合里。集合里就是循环继承的类。
    2. 重复继承:接口重复继承接口,对于一个接口,先dfs判断所有的父接口如果有重复继承,那这个接口也重复继承;再判断父接口继承的接口集合之间有没有交集,有交集则重复继承。类重复实现接口,对一个类,先dfs判断父类如果重复实现,或接口重复继承,那这个接口也重复继承;再判断父类实现的接口集合和接口继承的接口集合之间有没有交集,有交集则重复继承。

    二、四个单元中架构设计的演进  

    • 第一单元:带三角函数嵌套的表达式求导。

    我的架构是给每一种运算写一个类,回看代码有很多面向过程的写法,几个运算类之间的联系比较混乱。代码复用性很不好,后两次作业全部重构了。第三次作业难度激增,由于对嵌套运算的不合理递归和for循环,很多点都超时了。这是因为我过于看重架构实现,忽略了算法选择,按照自己的方法一写到底。

    • 第二单元:多线程电梯调度。

    这个单元我认识了几种设计模式:生产消费者模式,工厂模式,单例模式。电梯问题中的调度器是单例模式,考虑的是代码安全性;三个主要的类构成生产消费者模式,电梯是消费者,请求输入是生产者,调度器进行请求分配。第一次的分配算法用了傻瓜电梯的先到先服务方法,第二三次是主请求的捎带方法,第三次还需要考虑换乘问题。我前两次作业的复用性很好,第三次作业写的飞快,正确性也有保障。我总结出提高前期代码拓展性的方法是,架构设计越清晰越好,函数职责越简单越好,线程的交互越少越好。比如电梯的第二次作业,所有分配任务都是调度器完成的,电梯只需要上下楼开关门,并从调度器内部的一个队列里取出第一个的请求。第三次作业增加到三部电梯,变化只在调度器现在需要维护三个请求队列,三架电梯从各自队列取请求,电梯类不用修改。

    • 第三单元:JML规格。

    JML相比自然语言优越在其简洁性,逻辑性和无二义性。熟练掌握JML是第三单元的学习目标。

    这次作业的类也是层次化的,节点和边是最底层的类,上面一层是Path类,最上面的RailwaySystem类。另外封装一个计算最短路径的算法类。三次作业的区别就是图该怎么建的问题,第三次引入换乘问题,我使用了拆点法。如果数据结构忘的差不多了,这个单元就该还债了。最短路径算法有dijkstra和folyd可选,我三次作业一直沿用了dij算法,没有堆优化的dij是没有灵魂的dij,写着没有灵魂的dij我几乎T了第三次强测所有点。分析算法的复杂度应该成为我的一个习惯,不能只满足于通过中测,应该持续探索优化的空间。

    • 第四单元:解析UML图。

    这次我理解源码和指导书花费了大量时间,代码实现起来反而简单,类的结构是高度层次化的。顶层类一级级调用底层类的函数,完成一个查询功能。在思考了几种算法的可行性之后,仍然选择了最开始想到的dfs。这两个单元的作业复习了图结构的很多经典算法,包括最短路径,可达节点,寻找环路,寻找根节点。

    我注意到一个规律,对原始数据的预处理方式,严重影响后续实现的复杂程度。第三次作业搭建地铁路线的时候,如果每新加一条路径,都添加到现有的图上会很复杂,重新建图反而简单,时间也不会耽误太多。第四次作业对于所有UML元素,第一次遍历先将元素分类,再逐类处理更简单。

    三、四个单元中测试理解的演进

    从构造方法分类,分为人工构造测试集和自动生成测试集。从测试目的分类,分为正确性测试和压力测试。

    第一次作业是表达式求导,适合人工构造和自动生成相结合。先人工构造一些特殊的点,比如WRONG FORMAT的个例。然后自动生成复杂嵌套的测试点,测试求导的正确性。构造极端数据点进行压力测试也是必要的,可以揪出爆栈问题。

    第二次作业是多线程电梯,最适合对拍,用随机生成的一些电梯请求测试。

    第三次作业是JML规格,认识了JUnit插件,可以自动生成一些边界条件上的测试用例。其他测试方法无法相比的一个优点是,JUnit可以针对每一个方法测试,这样发生了错误更好定位。TLE是这次作业最大的问题,构造压力测试很重要。

    第四次作业是UML图查询,需要用staruml画图生成测试数据。画图要考虑全面所有情况,比如循环继承的继承环应该有所交叠,单继承多继承都出现。

    四、总结自己的课程收获

    1. 从面向过程转变成面向对象。
    2. 获得了完成千行级代码的工程能力。
    3. 写出有拓展性的代码,方便功能拓展。
    4. 用插件分析代码复杂度。
    5. 自动生成测试用例。
    6. JML规格描述功能实现。
    7. UML图整理架构思路。
    8. 寻找算法资料,优化设计的能力。

    五、改进建议

    1. 理论课增加JAVA易混淆知识点的讲解,少一些工程思想的介绍。十二次写代码作业把我们的实践能力练习的很充分,但知识上有很多漏洞。类继承和接口实现有很多繁琐的知识点我们始终没有触及,我在几次作业中应用到的继承也是特别简单的继承结构。有时候架构不好也不是思考的少,而是从练习中学习的这种教育模式,让我们只有用到了才去查,浅尝辄止,基础没有巩固,不如老师提前点播一下。
    2. 上机课的题目应该公布出来,有些课上没有完成的同学,之后还能继续研究。感觉我每次上机都是慌里慌张,从来没有时间好好研究一下上机的那些问题。上机过去就过去了,也没有条件细想,收获很少。
    3. 在每个单元的第一次作业时就公布三次作业的大致需求。我完成第一次作业时,不知道该怎么设计架构才能方便后面的拓展,因为不知道还要增加哪些功能,提高拓展性就是盲猜。有时候写一个性能最优但实现复杂的算法,反而是最不好拓展的。
  • 相关阅读:
    [CodeForces]Codeforces Round #429 (Div. 2) ABC(待补)
    About Me
    2018-06-14
    Codeforces Codeforces Round #484 (Div. 2) E. Billiard
    Codeforces Codeforces Round #484 (Div. 2) D. Shark
    Codeforces Educational Codeforces Round 44 (Rated for Div. 2) F. Isomorphic Strings
    Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes
    Codeforces Avito Code Challenge 2018 D. Bookshelves
    Codeforces Round #485 (Div. 2) D. Fair
    Codeforces Round #485 (Div. 2) F. AND Graph
  • 原文地址:https://www.cnblogs.com/mollygarden/p/11073844.html
Copyright © 2011-2022 走看看