OO——第三次总结

面向对象第三单元博客

本单元的主要内容为根据JML规格实现相应的接口。因为项目的大致框架都已经给出了现成的,不需要自己再去构思算法,所以上手较为简单。但需要着重考虑细节的优化,因此过弱中测较容易,而过强测较难。

一、实现规格采取的设计策略

1.从整体出发,大致把握各类所实现的功能和类与类之间的联系

  • 本次作业要求建立一个模拟社交网络,主要的对象是人(Person)和信息(Message),围绕两个对象有Group和Network两个维护关系的类。其中Network类最为重要,负责了绝大多数方法操作。

2.细读各个方法的规格,了解其作用

  • 本次作业主要有对象的增加、删除、查询操作,对对象集合进行维护并在适当时机跳转到异常类。
  • 对规格所描述的要求进行提炼,了解它要求的“本质”。

3.思考选择合适的容器和实现方法

  • 根据操作的频率选择储存的容器。
  • 对一些算法做封装。

小技巧:

  1. 大多数情况下根据方法的名字就可以猜出方法的内容。如addPerson方法,顾名思义就是进行一个简单的add操作,但JML规格却用了一大段的描述。掌握这个技巧可以有效提高阅读JML规格的效率。

  2. JML规格在描述时通常将normal_behavior放在最开头,这样require语句就会很长,而我们在实现时可以先实现exceptional_behavior,最后用else来概括所有的normal情况,这样可以减少错误概率。

二、基于JML规格来设计测试的方法和策略

  • 正确性测试

主要用来判断程序的基本功能有没有实现,正确性测试相当重要,我的强测因为正确性扣了不少分。而正确性主要可以通过测试复杂方法(isCirle等)、异常类来进行。
以下是进行正确性测试的一些方法。

  1. 对拍
    比较基本的测试手段,通过生成随机数据与别人的程序对比结果来验证程序的正确性。但数据具有随机性,不能完全保证程序的正确。

  2. JUnit
    JUnit是一个Java语言的单元测试框架。使用时需要对程序的内部实现有所了解,手动构造样例并判断程序运行的结果与预期是否一致。缺点是需要手动构造样例,数据量较小。

  3. OpenJML
    OpenJML能够对JML规格的完整性进行检查。

  • 性能测试

本次作业性能相当重要,弱中测对性能几乎没有要求,而强测对性能要求极高。因为经过研讨课后我直接采用了性能较好的方法,所以没有自己进行性能测试的过程。但我想大概可以用随机生成大量数据来检验程序运行的时间,具体可以用python实现。

三、容器选择和使用的经验

  1. 容器选择经验
  • 数据类型的选择
    本单元作业数据的大小并不苛刻,也不涉及超大数的运算,使用普通的int类型即可

  • 数据集合类型的选择
    本单元作业中NetWork类里需要存储Person数据的集合、Group数据的集合、Message数据的集合,需要频繁地进行插入、删除、查询操作,且每个数据都有各自独一无二的id,因此使用<Integer,xxxx>的HashMap来存储数据集合。
    private HashMap<Integer, Person> people = new HashMap<>();
    private HashMap<Integer, Group> groups = new HashMap<>();
    private HashMap<Integer, Message> messages = new HashMap<>();
    private HashMap<Integer, Integer> father = new HashMap<>();
    private HashMap<Integer, Integer> rank = new HashMap<>();
    private HashMap<Integer, Integer> emojilist = new HashMap<>();
    Person类中的messages集合是有序的,因此用arraylist存储比较方便。
    private ArrayList<Message> messages = new ArrayList<>();
    Person类中acquaintance和value具有一一对应的特性,因此并不完全按照JML规格去定义两个Hashset,而是用一个<Person,Integer>的HashMap来存储。
    private HashMap<Person, Integer> acquaintance = new HashMap<>();

  1. 容器使用的经验
  • HashMap的containValue方法复杂度为O(n),和arraylist没有区别,尽量不用。
  • 注意HashMap中按条件遍历删除不能用for循环,建议使用removeIf方法。

四、性能问题

本次作业的性能问题主要有以下三种情况:

  1. 容器选择不当
    我在第一次作业便犯了这个错误(不过因为有其他错误导致强测结果全是wa),使用了复杂度较高的arraylist。如果采用HashMap的话可以将插入、删除、查询操作均降到O(1)复杂度。
  2. 中间变量的维护
    作业中有查询年龄平均数和方差的操作,如果严格按照JML规格来需要每次都遍历所有的数据。不如在add、del操作中就顺带维护一个总年龄变量,可以将复杂度降低。单元结束才意识到这个问题orz
  3. 算法的选择
    本单元主要需要考虑算法的方法是isCircle和sendIndirectMessage
    isCircle在第一次作业中使用了朴素的DFS算法,第二次开始改为并查集算法。
    sendIndirectMessage选择较多,我使用了dijkstra算法,由于时间原因没有使用堆优化。

五、作业架构设计

  • 整体架构
    基本按照JML规格完成,中间少数细节为了性能进行了改进。

  • 异常类架构
    用静态变量x存储被访问的总次数,静态变量HashMap记录id与id触发异常的次数。在异常类内进行x和hashmap的计算。基本所有异常类都是同一个架构。

  • 图模型构建和维护策略

  1. isCircle方法中采用了并查集的算法,因此使用了树状结构,每个节点记录其父节点,按照并查集的一般算法进行维护(合并、查找)
  2. sendIndirectMessage方法采用了dijkstra算法,因此用邻接表存储点之间的关系。领接表的建立在Person类中由acquaintance-value的hashmap实现,查询由queryValue方法实现。
posted @ 2021-05-30 22:19  NoMansLand  阅读(50)  评论(1)    收藏  举报