【面向对象】第四单元UML总结及面向对象课程学期总结

1、第四单元的架构设计

第一次UML做业

第一次做业比较简单,仅包含类图的解析。正确理解UML元素的含义,以及每种UMLElement的各个属性的所指向的东西,就能比较容易地完成这次做业。算法

这里我构建了两个类:ClassRelations以及InterfaceRelations用来存储类和接口所包含的属性、方法等类图的基本信息,同时还要保存父类(父接口)、实现的接口,用来递归查找。是一种比较容易想到的实现方式。编程

类图以下:设计模式

为了加快对ClassName的查找和判断,使用了一个邻接表来存储同名的类,方便查找相应的类。安全

private HashMap<String, ArrayList<UmlClass>> classNameMap = new HashMap<>();

    private void checkClassName(String className)
        throws ClassNotFoundException, ClassDuplicatedException {
        if (!classNameMap.containsKey(className)) {
            throw new ClassNotFoundException(className);
        } else if (classNameMap.get(className).size() > 1) {
            throw new ClassDuplicatedException(className);
        }
    }

第二次UML做业

第二次做业在第一次做业的基础上,增长了对UML状态图和UML时序图的解析。此外,还增长了三个模型有效性检查的规则,分别是:不含重名的成员、不能循环继承、不能重复实现接口。bash

与第一次做业相比,总体的架构变化不大,总的思想仍然是把每一种UML元素放到对应的UML图中,根据需求对这些数据加以维护。总体稍加改动后直接继承上次的做业。多线程

本次实现的MyUmlGeneralInteraction直接继承了上次的MyUmlInteraction。对于状态图和时序图,与上次做业相似的,我建立了两个类StateMachineRelationsSequenceRelations用来保存与状态图和时序图相关的信息。架构

类图以下:并发

对于模型有效性的检验编程语言

  • R001:针对下面给定的模型元素容器,不能含有重名的成员(UML002)

    与检测类名重复相似的,在ClassRelations类中,添加属性和关联对端的时候,对同名的属性和关联对端进行计数,就能够得到找到重名的成员。

  • R002:不能有循环继承(UML008)

  • R003:任何一个类或接口不能重复继承另一个接口(UML009)

    这两个规则都是和继承或接口的实现相关的,因此放在一块儿处理。继承/实现关系构成了一个有向图,循环继承和屡次实现接口的优先图,具备以下特色:

    • 循环继承的充要条件是:有向图中存在环(节点到节点自身存在一条路径)
    • 重复继承的充要条件是:有向图中,一个节点到另外一个节点存在不止一条路径

    因此,将边权定义为优先图节点之间的路径数目,那么也就能同时判断是否存在循环继承和重复继承

    对于节点i到节点j的路径数目,对于中间节点k,存在以下的关系:route(i, j) = route(i, j) + route(i, k) * route(k, j)

    (貌似是动态规划的状态转移?有没有算法大佬能够讲一下,没学过不是很懂。顺便期待下学期算法有一个好的收获)

    使用UmlClassOrInterface接口对类和接口的关系统一建模,具体的代码以下:

    private HashMap<UmlClassOrInterface, HashMap<UmlClassOrInterface, Integer>>
                extensionGraph = new HashMap<>(); //包含继承关系和接口实现关系
    
      public static <T> void floyd(HashMap<T, HashMap<T, Integer>> graph) {
            for (T k : graph.keySet()) {
                for (T i : graph.keySet()) {
                    if (i.equals(k) || !graph.get(i).containsKey(k)) {
                        continue;
                    }
                    int ik = graph.get(i).get(k);
                    for (T j : graph.keySet()) {
                        if (k.equals(j) || !graph.get(k).containsKey(j)) {
                            continue;
                        }
                        int kj = graph.get(k).get(j);
                        if (!graph.get(i).containsKey(j)) { // 更新路径数
                            graph.get(i).put(j, ik * kj);
                        } else {
                            int newCnt = graph.get(i).get(j) + ik * kj;
                            graph.get(i).put(j, newCnt);
                        }
                    }
                }
            }
        }
    
      protected Set<UmlClassOrInterface> check008() {
            Set<UmlClassOrInterface> set = new HashSet<>();
            for (UmlClassOrInterface i : extensionGraph.keySet()) {
                if (extensionGraph.get(i).containsKey(i)) {
                    set.add(i);
                }
            }
            return set;
        }
    
        protected Set<UmlClassOrInterface> check009() {
            Set<UmlClassOrInterface> set = new HashSet<>();
            for (UmlClassOrInterface i : extensionGraph.keySet()) {
                for (UmlClassOrInterface j : extensionGraph.get(i).keySet()) {
                    if (extensionGraph.get(i).get(j) > 1) {
                        set.add(i);
                    }
                }
            }
            return set;
        }

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

架构设计是面向对象这门课的重中之重。因为咱们的课程是按照单元推荐,每一个单元的每一次做业都是在前一次的基础上,进行增量任务,因此一个好的架构可让后续的做业更加容易完成。

  • 第一单元:多项式求导

    前两次的做业由于需求还比较简单,还算是有一个比较看得过去的架构。把多项式分解为单项式,再把单项式分解成幂函数和三角函数,一个多项式的求导问题就被分而治之。就是下降耦合,抽象出各个不一样的类,将它们独立出来,再把每一个对象组合在一块儿来统一处理。

    可是随着第三次做业的发布,这个架构设计的弊端就显现出来了。扩展性较差,致使第三次做业加上嵌套求导以后,几乎推倒重写。仍是说明前两次做业对面向对象机制的理解不够深入,以及对Java语言掌握不够熟练(不会使用接口泛型等特性)

  • 第二单元:多线程电梯

    多线程电梯调度主要就是要理解清楚生产者-消费者模型以及发布-订阅模型这些课上讲到的重要的多线程编程的设计模型。多线程最重要的问题就是线程的同步与互斥、线程间通讯的问题。

    第二单元多线程电梯调度主要运用的就是“生产者-消费者模型”,构建一个两级的关系,经过共享队列在线程间传递信息。输入线程与调度器线程经过共享队列交互,调度器线程再与电梯线程经过共享队列交互。这个架构设计延续了三次做业,主要的变化都是调度器内部和电梯内部的调度算法。

    这个单元,课上老师也介绍了一些设计模式,好比单例模式、工厂模式等等。虽然实际写代码的时候没有特别注意使用这些设计模式,可是这些学习设计模式的思想,也是在知道咱们怎么去设计类,怎么去设计接口,使得程序具备良好的扩展性和鲁棒性。固然,最重要的仍是理解了多线程编程的方式和要点。

  • 第三单元:JML规格设计

    Java Modeling Language 是用于Java语言建模的语言,是一种契约化的设计思想。这个单元主要是围绕着图论展开的,因为有JML规格做为提示,总体比较顺畅。做业难度依次递增,从路径到地铁图层层递进。将不一样的需求(最短路径,最少票价,最少换乘)分别用不一样类的建模完成,下降耦合。因为都是图,因此把通用的图的算法单独提出来放到一个类,做为静态方法来调用(其实这样是有点危险的,一旦这里出错,全部地方都出错了,呜呜呜我就错在这了)

    JML为程序的开发设计提供了一个统一的规范规格,虽然估计JML实际应用不是不少,JML描述的规格,对方法、类等程序单元进行了严格的约束,这些正确详实的规格,相较于天然语言,能更加规范地描述需求,减小歧义,保证开发的速度与质量。这种”先设计规格再实现“的约定对于大型工程的协同开发有不少的好处。

  • 第四单元:UML图解析器

    这一单元主要的任务是解析UML图中的各类元素。理清楚UML各个元素的各个字段的意义以及之间的关系之后,推动的就比较顺利。

    根据UML图的组织形式,能够很容易地联想到仿照UML图地组织形式来构建每一个类,按照类图、顺序图、状态图分别构建模型和存储相应数据。把实现的各部分划分红不一样的责任单元,创建各个类来分别负责完成各自的任务。与第三单元有些相似。

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

  • 第一单元:多项式求导

    学习了讨论区编写测试脚本的方法,我采用以下的方式来发现别人的Bug:

    1. 把全部人的代码编译打包为.jar文件
    2. 编写测试数据(人工或自动生成),保存在testData.txt文件中
    3. 编写bash脚本,在Windows平台使用Git bash执行
    4. 编写Python脚本sympy用来计算标准答案,以及互测屋全部输出的答案
    5. 将第三步和第四步的数据输出保存到log.txt文件中,人工比对

    使用到的工具包括:Python Sympy, Git bash, VS code等。

    第一单元因为没有官方包的介入,输入输出都是本身处理,因此WRONG FORMAT是测试的重中之重。

  • 第二单元:多线程电梯

    多线程编程比较特殊,因为多线程并行具备不肯定性,且不一样的调度算法会形成不一样的输出结果,因此不存在惟一的正确答案,评测机实现起来也有点复杂。因此我自测阶段,没有进行大量数据的测试。

    互测阶段,主要是读代码,看有没有出现一下几种问题:

    • 线程安全问题(共享资源有没有上锁)
    • 轮询,容易形成CPU超时
    • 调度算法,容易形成运行总时间超时
  • 第三单元:JML规格设计

    第三单元使用了很重要的一个测试工具:JUnit。

    单元测试是一个强有力的测试工具。相比大量数据的黑盒测试,JUnit单元测试能够在更快速的找到代码漏洞,花费更少的时间,达到很好的验证正确性的效果。JUnit还有一个有点是有测试覆盖率的指标,这是随机数据的测试所不能比拟的。

    除了使用JUnit进行测试,还构造了一个比较强的随机数据生成器来进行测试,与同窗的输出进行比较。(仍是晚了,发现Bug的时候已经截至了)

  • 第四单元:UML图解析器

    第四单元用StarUML花了几个比较特殊的图,好比重复继承、循环继承相关,以及带多的环的状态图等特殊状况,来测试代码的正确性,并重点测试了几个用到了深度优先搜索算法的指令。期末考试也比较忙,测试频次不是不少。

测试先行是我印象最深的。

不管是什么工程,写代码、连电路、焊板子这些,最重要的都是进行全面的测试。测试必定要和编写代码同步进行,或者先于写代码完成。第三单元最后一次做业惨痛的教训让我记住了“测试先行”这个道理,不要等到最后再匆匆忙忙测试而后提交,一个隐蔽的错误形成的可能就是满盘皆输。

4、总结本身的课程收获

  • 优秀的IDE是事半功倍的前提:IDEA真的很好用
  • 能较为熟练地使用Git、GitLab、UML等现代工具
  • 比较熟练的掌握了一门面向对象的编程语言:Java,以及良好的代码风格
  • 了解并掌握了多线程并发编程的编程方式和技巧
  • 收获了面向对象的程序设计思想、原则
  • 基本的软件测试意识、能力、方法技巧,以及如何作一个狼人
  • 软件架构设计意识和一些经常使用的设计模式,好比工厂模式、单例模式等
  • 抗压能力和加班能力没有周末,是为之后996作准备
  • 迭代开发的能力,以及做为一个成熟地乙方应该要安心接受甲方(助教组和老师)的需求变更

一个学期的OO课程终于结束啦。就像登山同样,如今到了山顶终于能够喘口气了。每周的做业走在催逼着本身不断前进,虽然一个学期基本没过过一个舒服完整的周末,但回过头来看,这一万多行代码,每一个单元博客的总结记录,看获得这一路上的进步和收获。虽然有作的不尽人意的地方,但仍是蛮有成就感的。

至少最后表彰总结课上没有空手回去😁

5、对面向对象课程的建议

  1. 开个测试分享区,能够分享测试机或者测试数据

  2. 第二个是但愿互测能有些改变,这个我在第一单元的博客中也写到过。

    这三次的互测后,我相对如今的互测制度提一点小小的建议:

    1. 仍然划分A, B, C三档,可是分组把这三组混合编组,例如8人间能够{2A, 3B, 3C},7人间{2A, 3B, 2C}
    2. 找到某个等级做业的Bug得对应等级得分,而与本身的做业等级无关
    3. 这样作得好处是:每一个人均可以看到不一样水平的代码,给C组和B组的同窗更多学习的机会;避免了高段位“大眼瞪小眼”的尴尬,还有低段位“菜鸡互啄”的无趣;找到高段位的Bug更有成就感和分数奖励
    4. 缺点是:规则较为复杂,实现起来可能比较麻烦,并且不必定每一个人都接受这种制度

    有同窗说:“通过这三次互测,个人bash脚本和Python 写的比原来好多了。”

    固然,这些都只是我我的的想法,抛砖引玉,还请助教组学长学姐和老师们能研究出更好的制度,回归互测的本质。

    或者简单一些,把互测的人数减小到5至6人可能会合适些,这样能够多读代码,以避免一些同窗互测阶段直接放弃。

  3. 研讨课参与程度不过高,感受“研讨”的氛围不是很明显。

  4. 感受第三单元能够提到第一单元来。特别是多项式求导做为第一单元,并且最后一次做业,确实有点困难,不如先从JML开始,逐步熟悉Java语言和面向对象的建模方式。

相关文章
相关标签/搜索