BUAA-OO-2024 第三单元总结(JML 与 形式化验证)

沈锎-22373080 学生 2024-05-16 15:30:49

目录

  • 本单元内容概述
  • 针对本单元的测试过程
  • 黑箱测试与白箱测试
  • 具体测试方法
  • 数据构造策略
  • 模型构建与维护策略
  • 性能问题、规格与实现的分离
  • 数据结构的设计与选择
  • 相关算法的实现
  • 遇到的性能问题
  • 规格与 JUnit 测试
  • 学习心得体会

本单元内容概述

本单元的编码内容以社交关系网络为主,要求我们处理人际之间的关系,构建社交网络,同时增加标签对关系进行分组。在第三次作业中,针对人际关系增加多种消息,可以执行点对点发送,也可进行群发。

主要的功能通过 JML 进行功能规约,要求我们阅读 JML 并将其转化为合适的 Java 代码,需要我们选用恰当的数据结构、并展开恰当的算法。

同时,此单元也更加看重对于 JUnit 的使用,要求我们编写 JUnit 测试代码,并能够检测课程组提供的 Bug 代码。

针对本单元的测试过程

这里的测试有多个方面,一方面是对于自己代码的测试,另一方面是对于房友的代码进行测试,此外,在这个单元,我们还需要对针对 JUnit 测试提供的代码进行“盲测”。

针对这些情景,我们也许可以采用不同的测试策略。

黑箱测试与白箱测试

黑箱测试 黑箱测试隐藏代码的具体实现,而是站在一个用户的视角审视软件的功能。基于软件应有的规格描述,通过数据驱动进行测试。检查程序的输出与预期输出是否相符来进行测试。

白箱测试 白箱测试面向代码的具体实现,是针对具体代码的一种测试。白箱测试的常见形式是覆盖率测试,包括代码覆盖率、分支覆盖率等等。白箱测试在这一单元的作业中应用并不多。

具体测试方法

单元测试 是对软件中最小的可测试单元进行检查和验证,可以是一个方法、一个类、一个子系统等等。单元测试包括两种,人工静态检查与动态执行跟踪。

功能测试 根据产品特性,描述与用户场景、测试产品的特性与操作行为,以确定其是否满足要求。

集成测试 在单元测试的基础上,测试各个单元按设计组装成模块、子系统、系统的过程中,各部分工作是否正常,是否能够达到或实现预期的要求。

压力测试 在极其沉重的负载条件下,测试软件的健壮性和错误处理能力,可以评估软件在极端条件下的工作情况。

回归测试 用于验证软件的更改是否引入了新的错误,或者对之前已有的功能产生负面的影响。在修复程序错误的过程中也需要进行回归测试。

数据构造策略

在本单元的测评中,因为各种乱七八糟的事情堆积,并没有完整地完成一个本地测评机,但仍然进行了相关的一些工作:

对拍服务 重新使用寒假编写的对拍器,可以针对某个输入数据与小伙伴们对拍,便于针对数据进行 Debug 的过程。

本地测评对拍器 使用 Unit-1 独自编写的对拍器的框架,植入由 gpf(大佬赛高!) 编写并开源的数据生成器进行对拍。同时,我也针对一些边界情况编写了专门的生成器。

通用的数据构造 我认为可以采取这样的思路:统一生成人、关系、Tag、Message,再进行相应的删除与查询,这两个过程交织进行,可以考虑在数据生成器中也实时维护关系网络,以辅助删除、查询的高效进行。

边界的数据构造 无非两种情况,卡住下边界和卡住上边界,构造一些为零、为空的情况,构造一些达到上界的情况(比如 Tag 的 1111 的限制)。此外,这里还应该有一种特殊情况,id 碰撞,因为某个 id 的某个东西被删除后,就相当于消失了,这时,如果再增加一个同 id 的东西,会造成一些问题,事实也确是如此。

模型构建与维护策略

整体的类图如下所示:核心是 Network -> Person -> Tag 的层次化管理结构。同时,Network 也需要维护很多东西。

img

在这次的设计中,我使用 DisjointSet 类负责并查集的相关内容,包括插入、查询、重建(删边),这主要是针对第一次作业中的关系网络,主动维护熟人关系。

第二次作业新增了关系标签。在 MyTag 类中,主动记录年龄均值、年龄平方均值、ValueSum,以便快速查询。在增加人、删除人(从 Tag 中)时,会触发对缓存的修改

为了方便对关系与 Tag 的维护,我在 MyPerson 类中记录了该人所属的 Tag 的列表,并使用 attachTagdetachTag 方法进行统一接口的维护。在两个人之间的关系被移除时,会遍历人所属的 Tag,并判断是否应该断开 Tag 连接。

第三次作业新增各种消息,其中有一种是 EmojiMessage,当某个 emoji 被删除时,相应的消息也应该被移除,为了更好地维护这一点,我设计了一个专门的类 EmojiManager 用来管理全部的 emoji,并维护很多数据结构,通过回调函数进行消息的删除。

性能问题、规格与实现的分离

规格与实现的分离主要体现在两个方面:数据结构的设计差异算法的实现

规格只是规定了模型中使用的结构,但并强迫我们在实现中也采用相同的数据结构,我们可以在满足规格的条件下选择合适的数据结构,已进行算法层面的优化。同样的,规格也只限定了代码应该满足怎样的效果,但并没有限制我们的具体实现。

数据结构的设计与选择

在此单元的任务中,很多时候不需要考虑容器内对象的顺序,因此可以选用一些更加适合查找的数据结构。

对于人、人内部的 Tag,我采用 HashMap 进行 id 与对象的对应存储,它可以以很低的时间复杂度实现插入、查找与删除。对于 Tag 内部的 Person 对象,我直接使用 HashSet 进行存储,并没有本质区别。

为了实现 isLinked 的快速维护,我使用并查集进行处理,具体分析见下一小节。

对于关系最好的人的维护,我使用优先队列 PriorityQueue,配合自定义的比较方法进行维护,当关系发生变化时,会相应地进行维护。

对于各类 Message,我在网络中使用 HashMap 进行记录,而在每个人内部使用 LinkedList 进行维护,因为这里需要保留原有的顺序

对于 NoticeMessage,我在每个人内部使用 HashSet 进行冗余处理,要求在其中的信息才为合法的通知信息。但这里除了一些小问题,我忽视了 id 的可复用性,但是这里并没有被测出来。

对于 EmojiEmojiMessage,因为需要考虑反删等情况,我使用的数据结构比较复杂(其实完全是多此一举),在专门的 emoji 的管理类 EmojiManager 中,有如下成员:

HashMap<Integer, Emoji> emojiMap 存储 emoji 的 id 及其对象;
HashMap<Integer, ArrayList<Integer>> messageIds 存储某个 emoji 对应的信息的 id 的列表;
TreeMap<Integer, HashMap<Integer, Emoji>> emojiHeatMap 存储不同热度对应的 emoji 的集合(可使用 HashSet 进行简化);

还是上面的问题,我忽视了 id 的可复用性,导致在通过 emoji 删除 message 时,出现意外删除。直接导致了互测首次出锅,真的很可惜,嘤嘤嘤 。・゚゚・(>д<)・゚゚・。

相关算法的实现

核心算法有二:并查集最短路

在开展并查集的合并时,为了避免特殊的插入序列导致路径压缩时递归过深,我采用按个数合并的策略,即,将子节点个数更少的点作为合并时的祖先节点,能够在某些特殊情况下降低搜索深度,但不会减少搜索次数

在进行最短路的计算时,我采用的是双向 BFS 算法,能够在一定程度上减少搜索的宽度。

这里很大地体现出了规格与实现相分离的特点。规格只是告诉我们这个方法应该实现怎样的功能,很多时候也只是将结果“长什么样子”告诉我们,具体怎么做得自行思考。

遇到的性能问题

因为前期维护设计得比较多,相关算法的时间复杂度也十分可控,在本单元中没有出现性能问题。

规格与 JUnit 测试

规格的存在可以很好的帮助我们对代码进行 JUnit 测试。

首先,对于规格所约束的不变式,我们只需要在调用待测试方法前后进行检查即可,而这是我们重点需要进行的。

比如第三次作业的测试的检查的一部分:

HashSet<Integer> msgIds = new HashSet<>();
for (Message msg : newMessages) {
    // 消息的 id 不应该重复
    assertFalse(msgIds.contains(msg.getId()));
    if (msg instanceof EmojiMessage) {
        // 热度小于 limit 的 emoji 不应该存在
        assertTrue(emojiHeats.get(((EmojiMessage) msg).getEmojiId()) >= limit);
    }
    msgIds.add(msg.getId());
}

for (Message msg : oldMessages) {
    // 热度不小于 limit 的 emoji 不应该被删除
    if (msg instanceof EmojiMessage
        && (emojiHeats.get(((EmojiMessage) msg).getEmojiId()) >= limit)) {
        assertTrue(msgIds.contains(msg.getId()));
    }
    // 不是 emoji 消息应该在
    if (!(msg instanceof EmojiMessage)) {
        assertTrue(msgIds.contains(msg.getId()));
    }
    // 将这个检查完的消息删掉
    msgIds.remove(msg.getId());
}

// 确保一一对应,不会凭空产生消息
assertTrue(msgIds.isEmpty());

可以发现,扣住规格进行测试可以更加清晰、更加方便。

但是,要想做好测试总归是要有数据的,如果数据过弱,可能导致很多问题暴露不出来。

我认为这里有一个很尴尬的地方:通过规格我们可以很轻松地判断一个方法的运行结果、副作用等是否符合要求,但这样单纯基于规格的检验,并不能确保在任何情况下,方法都可以按照我们的预期执行。

也就是说,规格也仅仅是帮助我们编写检查部分的代码而已。

学习心得体会

此单元的学习还是比较糟心的,一方面是 JML 晦涩难懂,且不够清晰、稳定,另一方面是 JML 与具体实现脱节太大。

很多时候,按照 JML 也可以很简单地完成代码,但是复杂度得不到要求,于是就不得不自行进行理解,并采用完全不同的方式编写代码。就这个层面而言,JML 反而是增加了理解的难度,降低了易读性,反而可能导致问题。

在编写代码的时候,我常常在思考,JML 到底是谁写给谁看的,人写起来费劲、看起来也费劲,也没有发现什么(或者说课程组也没有大力推广什么)软件能够自动的完成 JML 的完整检验,到头来还是人在为难人,也在为难自己

我充分理解这一单元的用意,JML 只是一个载体,规格与形式化验证是一个很重要的内容,也有着不可替代的地位,这我不否认,只是在怀疑,目前这种形式可能效果不是很好,也没有让我们能够意识到其优势。

因此我有两种建议,将规格与形式化验证保留在面向对象课程中,但是也需要继续改进,或者更换形式,否则未必能够让所有同学都接受。

此外,还可以直接更换单元主题,面向对象与工程开发还可以有很多话题可以研究,比如规范的开发文档规范的 git 提交信息等等,我认为也可以很有价值,只是目前的测评形式可能不是很好适应。

...全文
119 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
洪陈天一 助教 2024-05-16
  • 打赏
  • 举报
回复

开发文档和 git 提交信息等不应该属于面向对象设计与构造课程的范围,实际在大三下的软件工程课程中可以对这些进行学习与实践

【重要提示】本资源设置为0积分下载,若非0积分请勿轻易下载 亲爱的CSDN用户: 首先感谢你点进这个资源页面。我需要提前说明一个重要情况: 本资源原本已设置为“0积分下载”,即作者希望完全免费共享。但CSDN平台有时会根据文件的下载热度、文件大小、用户权限等因素,自动将部分资源的积分调整为非0数值(如1积分、2积分、5积分等)。这是平台系统的自动行为,而非作者本人的设定。 因此,如果你当前看到该资源的下载所需积分不是0(例如显示为1、2、3……),请谨慎决定是否下载。 如果你按照非0积分支付并下载后发现资源内容不符合预期、链接失效,或者实际上该资源本应是免费的,作者无法为此承担积分损失或退还操作。强烈建议:仅在页面显示为0积分时进行下载。 另外,本资源描述中并未直接提供具体的下载地址或外部链接,因为它本身是一个通过CSDN官方上传通道提交的文件/内容包。如果你看到描述中没有外部网盘地址,这是正常的——资源文件应通过CSDN内置的“下载”按钮获取。若因平台积分显示异常导致你支付了积分,请优先联系CSDN客服咨询积分退还政策,作者没有权限修改平台自动设定的积分值。 感谢你的理解与支持。技术分享本应开放,但受限于平台规则,特此提醒如上。祝学习进步!
内容概要:本文围绕“计及风电不确定性的电力系统黑启动与负荷恢复协同优化”开展研究,提出了一种融合风电出力随机性与波动性的黑启动及负荷恢复协同优化模型,并基于Matlab平台实现了完整的代码仿真与算例验证。研究构建了考虑不确定性因素的优化框架,通过场景生成与削减技术处理风电出力的随机特征,建立了兼顾系统安全性、恢复效率与供电可靠性的多目标优化模型。文中详细阐述了模型的数学建模过程、关键约束条件(如功率平衡、设备启停顺序、网络拓扑约束等)、求解算法设计及黑启动电源优选策略,并通过标准测试系统验证了所提方法在提升灾后恢复能力方面的有效性与优越性。; 适合人群:具备电力系统分析、优化理论及可再生能源并网基础知识,从事电力系统恢复控制、新型电力系统韧性提升、新能源集成等方向研究的研究生、科研人员及电力行业工程技术人才。; 使用场景及目标:①用于电力系统大面积停电后的黑启动方案制定,优化启动电源选择与负荷恢复时序;②为高比例风电接入背景下电力系统的安全稳定与弹性恢复提供决策支持;③作为Matlab仿真教学资源,辅助理解不确定性建模、随机/分布鲁棒优化及电力系统动态恢复过程。; 阅读建议:建议结合提供的Matlab代码进行同步研读,重点掌握风电不确定性建模方法(如场景法)、优化模型构建逻辑与求解流程(如使用YALMIP调用求解器),宜在熟悉基本电力系统运行与优化算法的基础上进行复现与拓展研究。

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧