敏捷软工结对编程博客

本文介绍了敏捷软件开发中结对编程的经验,重点关注接口设计和测试。通过详细的设计和实现过程,展示了如何进行接口设计以实现模块间的松耦合,以及如何利用单元测试和对拍测试确保代码质量。文章还涵盖了性能优化、异常处理、UI设计和结对编程的体会,提供了一个完整的项目案例。
摘要由CSDN通过智能技术生成

敏捷软工结对编程博客

项目 内容
这个作业属于哪个课程 2022春季软件工程(罗杰 任健)
这个作业的要求在哪里 结对编程项目-最长英语单词链
我在这个课程的目标是 学习软工的项目合作管理知识,提升软件开发技术
这个作业在哪个具体方面帮助我实现目标 学习敏捷开发中的PSP与结对编程的思想并付诸实践

Part0 准备

1.必要信息

教学班级:周五班

项目地址:https://github.com/BUAADreamer/Longest-English-word-chain

成员:18373466 战晨曦,19373573 冯张驰

2.PSP开发时间估计

PSP2.1 Personal Software Process Stages 预估耗时(分钟)
Planning 计划 90
· Estimate · 估计这个任务需要多少时间 90
Development 开发 790
· Analysis · 需求分析 (包括学习新技术) 60
· Design Spec · 生成设计文档 30
· Design Review · 设计复审 (和同事审核设计文档) 30
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10
· Design · 具体设计 60
· Coding · 具体编码 360
· Code Review · 代码复审 60
· Test · 测试(自我测试,修改代码,提交修改) 180
Reporting 报告 100
· Test Report · 测试报告 60
· Size Measurement · 计算工作量 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30
合计 980

Part1 设计

3.接口设计理念

接口,对于团队开发来说至关重要。只有确定了接口,才能够在开发时让接口的实现者与接口的调用者之间的交流尽可能少,有利于提高效率;反之,如果接口模模糊糊,那么接口的实现者以及接口的调用者各自干的工作就很有可能冲突,少不了重构。在这个事情上我算是体验深刻:在结对进入互换模块阶段时,我们发现我们提供的计算模块接口和互换组的接口不一样(因为官方给的接口是废的,两个组在互相没通知的情况下各自重新设计了接口),这导致我们没法互换模块测试。为了能实现互换模块,当时我们花了 1 个小时改我们这边的计算模块的接口,这个过程很痛苦且没有意义。倘若最开始有一个稳定的接口且双方都严格遵守接口,那估计就没有这种返工了。而在两边接口重新确定后,不管我怎么修改接口的具体实现(比如修 bug,性能调优等),其他部分的代码都不需要改动(如果其他地方是对的话),并且互换模块的时候也不会带来额外的工作量,这样其实就是实现了松耦合,非常舒服。

另外在设计接口的时候,要尽可能做到信息隐藏。信息隐藏指的是在设计和确定模块时,使得一个模块内包含的特定信息(过程或数据),对于不需要这些信息的其他模块来说,是不可访问的。在数据方面,CalcuCore 是我们整个项目的计算核心,但是其构造方法传入的属性对外界并没有什么用处,所以我们把 CalcuCore 中的属性都设计成了 private。在过程方面,对 PairTestInterface 中的 4 个接口 API 来说,CalcuCore 中只有 4 个对应的方法需要被其知道,所以我们只把 CalcuCore 中的 4 个核心方法设计成 public;而这几个核心方法所调用的其他工具方法却对外界没有什么用处,我们也不希望外界知道 CalcuCore 内部有这么几个方法,所以工具方法我们都设计成了 private

4.接口/算法的设计与实现

接口设计

由于我们事先阅读了后面阶段的要求,并且预先找到了互换模块的小组,所以我们设计了适用于 C# 的类似于官方接口的接口,接口中每个方法的功能,和官方接口同名方法的功能相同。

接口设计如下:

public static int gen_chain_word(List<string> words, List<string> result, char head, char tail, bool enable_loop);

public static int gen_chains_all(List<string> words, List<string> result);

public static int gen_chain_word_unique(List<string> words, List<string> result);

public static int gen_chain_char(List<string> words, List<string> result, char head, char tail, bool enable_loop);

我们将这些接口方法整合到了 PairTestInterface 中。在本地测试时,我们通过 CmdTestInterface 接口去驱动测试,调用 PairTestInterface 提供的接口,而 PairTestInterface 提供的接口调用 CalcuCore 中的具体计算方法。

接口实现

接口方法的实现,实际上是首先通过参数去实例化 CalcuCore(这是我们真正的核心计算类),然后调用 CalcuCore 类中对应的真正的计算方法 ,之后对返回的结果做异常处理,体现了层次化的思想。例如:

  • 对于gen_chain_word ,我们用给定的参数 wordsheadtailenable_loop 以及一些 default 参数(辅助建图)去实例化一个 CalcuCore,然后调用 CalcuCore 中的 getMaxWordCountChain 方法进行具体的计算。
  • 对于 CalcuCore
    • 其构造方法会使用 gen_chain_word 给定的参数,进行参数化建图。
    • getMaxWordCountChain 首先会调用数据检查方法 dataCheck,去检查数据中是否有隐含环以及是否允许在有环的情况下求解;如果数据中没有环,则调用重构图方法 refactorGraph 对图进行预处理,接着调用快速算法 fastGetMaxWordCountChainDAG 上跑 dp 求解;如果数据中有环,且要求在有环的情况下求解,则调用暴力算法 trivialGetMaxWordCountChain 求解最长链。
  • 对于异常:
    • 主要是处理结果过长以及数据有环且不能求解这两种情况。

算法设计

首先,如果单词 A 的尾字母和单词 B 的首字母相同,则以 AB 为结点,连接一条从 AB 的有向边,在 O ( n 2 ) O(n^2) O(n2) 的时间内建立一张有向图。其中 n n n 是不同单词的种类数。有向图的边权我们并不关心,但是点权的设置要根据情况来:如果我们求解的最长链是以个数为指标,则点权为 1;如果是以字母数为指标,那么点权为单词的长度。

然后就是每个具体接口的求解算法:

gen_chain_word 的求解算法主要是 getMaxWordCountChain ,而 getMaxWordCountChain 的算法分为两部分。如果图是一个有向无环图,那么可以使用动态规划求解:

  • dp[word] 表示 wordword 结尾的单词链的最长长度,lastWord[word] 表示以 word 结尾的最长链的前驱结点。
  • 初始化 dp[word]word 的点权,lastWord[word] 为空(即没有前驱)。
  • 转移时,采用在拓扑排序的基础上进行状态转移的方式,假设 AB 的前驱,那么 dp[B] = max(dp[B], dp[A] + weight[B]),其中 weight[B]B 的点权。假如有 dp[A] + weight[B] > dp[B],那么还需要更新前驱,即 lastWord[B] = A
  • 最终统计答案时,需要看所有点的 dp 值,把最大的那个
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值