201971010216-李斌 实验三 结对项目——《{0-1}KP 实例数据集算法实验平台》项目报告
实验三 软件工程结对项目
项目 | 内容 |
---|---|
课程班级博客的链接 | 2019级卓越工程师班 |
作业要求链接 | 实验三 结对项目 |
课程学习目标 | 1.体验软件项目开发中的两人合作,练习结对编程。2.掌握Github协作开发软件的操作方法。 |
结对对方学号和姓名 | 201971010237-尚洁 |
结对方博客链接 | [实验三 软件工程](https://www.cnblogs.com/sj3191/p/16061576.html) |
项目仓库链接 | 实验三 仓库 |
任务1
阅读《现代软件工程—构建之法》第3-4章内容,理解并掌握代码风格规范、代码设计规范、代码复审、结对编程概念;
-
IC在团队中的流程
- 通过交流、实验、快速原型等方法,理解问题、需求或任务
- 提出多种解决办法并估计工作量其中包括寻找以前的解决方案,因为很多工作是重复性的
- 与相关角色交流解决问题的提案,决定一个可行的方案
- 执行,把想法变成实际中能工作的代码,同时验证方案的可行性和其他特性
- 和团队的其他角色合作,在测试环境中测试实现方案,修复缺陷(Bug )。如果此方案有严重的问题,那么就考虑其他方案
在解决方案发布出去之后,对结果负责
-
代码设计规范
- 概念:代码设计规范不光是程序书写的格式问题,而且涉及到程序设计、模块之间的关系、设计模式等方方面面,又有不少内容与
具体程序设计语言息息相关(如C,C++,JAVA,C#),但是也有通用的原则。 - 函数:原则:只做一件事,并且要做好。
- goto:函数最好有单一出口,为了达到这一目的,可以使用goto。
-
错误处理:
- 参数处理:在Debug版本中,所有的参数都要验证其正确性,在正式版本中,对从外部(用户或别的模块)传递过来的参数,要验证其正确性。
- 断言:验证正确性就要用断言。
- 概念:代码设计规范不光是程序书写的格式问题,而且涉及到程序设计、模块之间的关系、设计模式等方方面面,又有不少内容与
-
结对编程:
- 在结对编程模式下,一对程序员肩并肩地、平等地、互补地进行开发工作。两个程序员并排坐在一台电脑前,面对同一个
显示器,使用同一个键盘,同一个鼠标一起工作。他们一起分析,一起设计,一起写测试用例,一起编码,一起单元测试,
一起集成测试,一起写文档等。
- 在结对编程模式下,一对程序员肩并肩地、平等地、互补地进行开发工作。两个程序员并排坐在一台电脑前,面对同一个
-
角色
- 驾驶员(Driver ) :控制键盘输入。
- 领航员(Navigator ) :起到领航、提醒的作用。
-
优点
- 在开发层次,结对编程能提供更好的设计质量和代码质量,两人合作解决问题的能力更强。两人合作,还有相互激励的作用,
工程师看到别人的思路和技能,得到实时的讲解,受到激励,从而努力提高自己的水平,提出更多创意。 - 对开发人员自身来说,结对工作能带来更多的信心,高质量的产出能带来更高的满足感。
- 在企业管理层次上,结对能更有效地交流,相互学习和传递经验,分享知识,能更好地应对人员流动。
- 在开发层次,结对编程能提供更好的设计质量和代码质量,两人合作解决问题的能力更强。两人合作,还有相互激励的作用,
-
如何结对编程
- 驾驶员:写设计文档,进行编码和单元测试等XP开发流程。
- 领航员:审阅驾驶员的文档;监督驾驶员对编码等开发流程的执行;考虑单元测试的覆盖率;思考是否需要和如何重构;
帮助驾驶员解决具体的技术问题。 领航员也可以设计TDD中的测试用例。 - 驾驶员和领航员不断轮换角色,不要连续工作超过一小时,每工作一小时休息15分钟。领航员要控制时间。
- 主动参与。任何一个任务都首先是两个人的责任,也是所有人的责任。
- 只有水平上的差距,没有级别上的差异。两人结对,尽管可能大家的级别资历不同,但不管在分析、设计或编码上,
双方都拥有平等的决策权利。 - 设置好结对编程的环境,座位、显示器、桌面等都要能允许两个人舒适地讨论和工作。如果是通过远程结对编程,
那么网络、语音通讯和屏幕共享程序要设置好。
任务2:
两两自由结对,对结对方《实验二 软件工程个人项目》的项目成果进行评价
项目 | 内容 |
---|---|
结对方 | 尚洁(201971010237) |
实验二链接 | 实验二 软件工程 |
实验二仓库 | 仓库 |
博客评论
-
博文结构:
- 整体结构较为简练,条目清晰,但在排版上存在一定的欠缺。
-
博文内容:
- 总体上完成了要求的内容,以各任务为中心构成行文路线。但对于项目描述的详细程度有所不足,但是总体而言还是很不错。
-
博文结构与PSP中“任务内容”列的关系:
- 博文结构与“内容任务”列并非完全对应。博文结构主要应用“从面至点”的思路,由整体开发思路到详细编码内容。
-
PSP中“计划共完成需要的时间”与“实际完成需要的时间”两列数据的差异化分析与原因探究:
- 实际完成需要的时间比计划共完成需要的时间更长,作者提到其自身编码能力很好,使用Java进行编程,各个功能实现完整,实验整体非常成功。
Git操作
将项目导入进行测试
项目 | 内容 |
---|---|
概要部分 |
|
代码符合需求和规格说明么? | 完成全部功能 |
代码设计是否考虑周全? | 是 |
代码可读性如何? | 较好 |
代码容易维护么? | 易维护 |
代码的每一行都执行并检查过了吗? | 是 |
```设计规范部分```` | |
设计是否遵从已知的设计模式或项目中常用的模式? | 是 |
有没有硬编码或字符串/数字等存在? | 有 |
代码有没有依赖于某一平台,是否会影响将来的移植? | 对移植影响较小 |
开发者新写的代码是否用已有的Library/SDK/Framework中的功能实现? | 是 |
在本项目中是否存在类似的功能可以通过调用而不用全部重新实现? | 是,代码中使用了Matplotlib库 |
有没有无用的代码可以清除? | 没有 |
代码规范部分 |
|
修改的部分符合代码标准和风格么? | 符合 |
具体代码部分 |
|
有没有对错误进行处理?对于调用的外部函数,是否检查了返回值或处理了异常? | 已处理 |
参数传递有无错误,字符串的长度是字节的长度还是字符的长度,是从0开始计数还是从1开始计数 | 无错误;字符的长度;从0开始 |
边界条件是如何处理的?switch语句和default分支是如何处理的?循环有没有可能出现死循环? | 前提分析推导边界条件;可能 |
有没有使用断言来保证我们认为不变的条件真的得到满足? | 否 |
对资源的利用,是在哪里申请,在哪里释放的?有无可能存在资源泄露?有没有优化的空间? | 有对应的申请、释放语句;不存在;有 |
数据结构中有没有用不到的元素? | 有 |
效能 |
|
代码的效能如何?最坏的情况是怎么样的? | 效能一般;最坏情况下回溯算法将在极长的一段时间无法给出结果 |
代码中,特别是循环中是否有明显可优化的部分? | 有,如动态规划可优化为一维 |
对于系统和网络的调用是否会超时?如何处理? | 否 |
可读性 |
|
代码可读性如何?有没有足够的注释? | 没有足够的注释,但代码较为清晰,故可读性较好 |
可测试性 |
|
代码是否需要更新或创建新的单元测试? | 是 |
结对方项目仓库中的日志数据 | |
Commits数据:如图所示,帮助结对方完成了背包数据读取与选择的功能,并上传至Github。 | |
Forks数据:如图所示,对结对方的项目进行了Fork操作和将修改提交到指定一方的仓库中
将修改提交到指定一方的仓库中
任务3:
采用两人结对编程方式,设计开发一款{0-1}KP实例数据集算法实验平台
-
项目背景:
- {0-1}背包问题:有N件物品和一个容量为M的背包。第i件物品所占空间是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大,对于每件物品,仅有取与不取两个状态。
-
需求分析:
- 通过需求文档,使用“3W”模型进行分析:
- Who-为谁设计,用户是谁?
- 为需要使用不同的方法求出0-1背包问题的最优解、提高多组数据处理效率的用户。
- What-要解决哪些问题?
- GUI页面:
- 采用java图形用户界面的方式实现GUI。
- 数据读入与处理:
- 正确读入实验数据文件的有效{0-1}KP数据,并将其处理为便于后续操作的数据形式。
- 数据展示(拓展):
- 选择任意一组已经读入的数据并展示。
- 散点图绘制:
- 绘制任意一组{0-1}KP数据以价值重量为横轴、价值为纵轴的数据散点图。
- 排序:
- 对任一组{0-1}KP数据按重量比进行非递增排序。
- 贪心算法:
- 采用贪心算法求解不超过背包容量的最大价值和解向量。
- 动态规划算法:
- 采用动态规划算法求解不超过背包容量的最大价值和解向量。
- 回溯算法:
- 采用回溯算法求解不超过背包容量的最大价值和解向量。
- 遗传算法:
- 采用遗传算法求解不超过背包容量的最大价值和解向量。
- 文件保存:
- 将求解时间,最大价值,解向量保存至文件中。
- 数据库保存:
- 将数据集、日志数据存入数据库。
- Why-为什么要解决这些问题?
- 根据给出的任务要求,结合使用系统时的实际需求,在该项目的实现中我们需要实解决上述的问题。
软件设计
- 1、D{0-1}KP 实例数据集需存储在数据库;
- 2、平台可动态嵌入任何一个有效的D{0-1}KP 实例求解算法,并保存算法实验日志数据;
- 3、人机交互界面要求为GUI界面;
- 4、查阅资料,设计遗传算法求解D{0-1}KP,并利用此算法测试要求(3);
遗传算法
-
遗传算法(Genetic>遗传算法(Genetic Algorithm,GA)是进化计算的一部分,是模拟达尔文的遗传选择和自然淘汰的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。该算法简单、通用,鲁棒性强,适于并行处理。
-
遗传算法的基本步骤是:
- (1)初始化:设置进化代数计数器t=0,设置最大进化代数T,随机生成M个个体作为初始群体P(0)。
- (2)个体评价:计算群体P(t)中各个个体的适应度。
- (3)选择运算:将选择算子作用于群体。选择的目的是把优化的个体直接遗传到下一代或通过配对交叉产生新的个体再遗传到下一代。选择操作是建立在群体中个体的适应度评估基础上的。
- (4)交叉运算:将交叉算子作用于群体。遗传算法中起核心作用的就是交叉算子。
- (5)变异运算:将变异算子作用于群体。即是对群体中的个体串的某些基因座上的基因值作变动。群体P(t)经过选择、交叉、变异运算之后得到下一代群体P(t+1)。
- (6)终止条件判断:若t=T,则以进化过程中所得到的具有最大适应度个体作为最优解输出,终止计算。
参考文献:
- [1]吴聪聪,贺毅朝,赵建立.求解折扣{0-1}背包问题的新遗传算法[J].计算机工程与应用,2020,56(07):57-66.
- [2]杨洋,潘大志,刘益,谭代伦.折扣{0-1}背包问题的简化新模型及遗传算法求解[J].计算机应用,2019,39(03):656-662.
遗传算法伪代码,如下图所示。 -
软件实现及核心功能代码展示
实现图形用户界面功能:
public static void main(String[] args){
ArrayList<GradeFrame> GradeFrame = new ArrayList<GradeFrame>();
int arr[] = {1,1,1,1,1,1,1,1,1,1};
JPanel p = new JPanel();
p.setPreferredSize(new Dimension(200, 180));
JFrame f = new JFrame("0/1背包管理系统");
f.setSize(400, 300);
JButton b1 = new JButton("输入题目要求");
JButton b2 = new JButton("输出实验结果");
JButton b3 = new JButton("输出散点图");
b1.addActionListener(new ActionListener(){
//单击输入执行的方法
public void actionPerformed(ActionEvent e) {
JPanel jp12=new JPanel();
JPanel jp13=new JPanel();
JTextField jt11=new JTextField(10);
JTextField jt12=new JTextField(10);
JDialog jd = new JDialog();
jd.setLayout(new GridLayout(10, 1));
jd.setTitle("请输入");
jd.setSize(400, 850);
jp13.add(new JLabel("所使用的算法"));
jp13.add(jt12);
jd.add(jp12);
jd.add(jp13);
JPanel jp1=new JPanel();
JPanel jp11=new JPanel();
JButton jb1,jb2;
JTextField jt1=new JTextField(10);
jb1=new JButton("确定");
jb2=new JButton("取消");
jb2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
jd.dispose();
}
});
jp1.add(new JLabel("背包数据数"));
jp1.add(jt1);
jp11.add(jb1);
jp11.add(jb2);
jd.add(jp1);
jd.add(jp11);
jb1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String num;
for(int i=0;i<10;i++) {
if(i==0) {
num = jt1.getText();
int x = Integer.parseInt(num);
arr[i]= x;
}
}
}
});
jd.setLocationRelativeTo(null);
jd.setModal(true);
jd.setVisible(true);
}
});
b2.addActionListener(new ActionListener(){
//单击输出执行的方法
public void actionPerformed(ActionEvent e) {
JDialog jd = new JDialog();
jd.setTitle("实验结果");
jd.setSize(300, 500);
JTextArea jt = new JTextArea();
jt.setLayout(null);
jt.setSize(300, 500);
for (bag.GradeFrame d :GradeFrame)
{
jt.append(d.toString()+"\r\n");
}
jd.add(jt);
jd.setLocationRelativeTo(null);
jd.setModal(true);
jd.setVisible(true);
}
});
b2.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
});
p.add(b1);
p.add(b2);
p.add(b3);
f.add(p, BorderLayout.SOUTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//界面关闭后程序结束
f.setLocationRelativeTo(null);
f.setVisible(true);
}
测试运行:
首页:
数据输入:
运行结果:1
运行结果:2
数据库:1
数据库:2
散点图:
结对过程记录:
此次结对实验的PSP
PSP2.1 | 任务内容 | 计划共完成需要的时间(min) | 实际完成需要的时间(min) |
---|---|---|---|
Planning | 计划 | 10 | 13 |
Estimate | 估计这个任务需要多少时间,并规划大致工作步骤 | 10 | 12 |
Development | 开发 | 400 | 580 |
Analysis | 需求分析(包括学习新技术) | 30 | 45 |
Design Spec | 生成设计文档 | 15 | 25 |
Design Review | 设计复审(和同事审核设计文档) | 15 | 24 |
Coding Standard | 代码规范(为目前的开发制定合适的规范) | 10 | 18 |
Design | 具体设计 | 140 | 160 |
Coding | 具体编码 | 40 | 80 |
Code Review | 代码复审 | 30 | 40 |
Test | 测试(自我测试,修改代码, 提交修改) | 30 | 38 |
Reporting | 报告 | 50 | 70 |
Test Report | 测试报告 | 20 | 24 |
Size Measurement | 计算工作量 | 16 | 24 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 10 | 15 |
实验总结:
-
对于此次结对编程实验,我有如下感受及体会:
第一次接触结对编程,对于结对编程的整体感受总的来说是利大于弊的,即有1+1>2的效果。我认为或许结对编程并不适用于简单的写代码的工作,结对编程更适用于解决一些方向性的问题,因为在编程时,每个人的思想不同可能用到的方法及语句也就不同,所以难免会产生分歧与不一致,但是,两个人的思想可能会碰撞出新的火花,有时候还可以找到更适合、更节省空间与时间的方法与算法。在结对编程中,双方的互动目的在于开启思路,避免单独编程时思维容易阻塞的情况,有时候一个人在写代码时,难免会陷入僵局出不来,而两个人合作缺可以解决不这个问题,有时候对方的一句话可以启发自己换一种思考方式,从而高效地解决问题。
在自己编程过程时不难发现,编程耗时最多的方面就是debug。在我们得出设计思路,并将它们初次转化成代码后,编程之路其实才走了一小段。由于个人的疏忽,输入的错误,以及设计思路的偏差,往往会让我们的程序陷入无止尽的bug泥潭中,难以挣脱,这会消耗我们大量的时间。 而结对编程的好处就在于此。由于身边有个领航员角色的存在,在编写代码时,一旦出现输入错误,就会有人及时的提醒。并且,在设计代码时,有个同伴可以一起讨论,融合两个人不同的见解和观点,我们往往可以得出更加准确且更加高效的设计思路。这一切都为我们在完成代码后的debug过程省去了大量的时间。
总体来说,结对编程可以很大程度上提高编程效率,而且两人轮流编程,不会太过疲惫,因此十分适合敏捷开发。如果未来我们从事软件开发的工作,我们会十分乐于进行结对编程,因为这会极大的改善我们的编程体验,是编程不再那么枯燥,debug之路也不会那么恐怖。