201971010101-阿丽米拉 实验三 结对项目—《{0-1}KP 实例数据集算法实验平台》项目
项目 | 内容 |
---|---|
课程班级博客链接 | 2019级卓越班 |
作业要求链接 | 实验三 |
我的课程学习目标 | 体验软件项目开发中的两人合作,练习结对编程;掌握Github协作开发程序的操作。 |
这个作业在哪些方面帮助我实现学习目标 | 学习如何与同学高效的分配并完成小组合作任务,帮我复习了如何使用python写图形用户界面,并让我了解熟悉了遗传算法。 |
结对方学号-姓名 | 201971010160-谢家俊 |
结对方本次博客作业链接 | 谢家俊 |
本项目Github的仓库链接地址 | Github的仓库 |
实验内容
任务一:阅读《现代软件工程—构建之法》第3-4章内容,理解并掌握代码风格规范、代码设计规范、代码复审、结对编程概念;
-
代码风格规范:简明、易读、无二义性。
-
缩进:4个空格。
-
行宽:可以限定100字符。
-
括号:在复杂的条件表达式中,用括号清楚地表达逻辑优先级。
-
断行与空白的{}行:格式D:每个“{”和“}”都独占一行。
-
分行:不要把多条语句放在一行上。
-
命名:在变量名中不要提到类型或者其他语法方面的描述。避免过多的描述。如果信息可以从上下文中得到,那么此类信息就不必写在变量名中。避免可要不可要的修饰词。
-
下划线:下划线用来分隔名字中作用域标注和变量的语义。
-
大小写:由多个单词组成的变量名,如果全部都是小写,很不易读,一个简单的解决方案就是用大小写区分它们。
-
注释:注释是为了解释程序做什么(what),为什么这样做(why)。
-
-
代码设计规范:
- 函数:最重要的原则:只做一件事,并且要最好。
- goto:函数最好有单一的出口,为达到这一目的,可以使用goto。只要有助于程序逻辑的清晰体现,什么方法都可以使用,包括goto。
- 错误处理:当程序的主要功能实现后,给代码加一些错误处理。
1、参数处理:在debug版本中,所有的参数都要验证其正确性。在正式版本中,对从外部(用户或别的模块)传递过来的参数,要验证其正确性。
2、 断言:使用断言来验证正确性。- 如何处理c++中的类:
1、类:
使用类来封装面向对象的概念和多态( Polymorphism)。
避免传递类型实体的值,应该用指针传递。换句话说,对于简单的数据类型,没有必要用类来实现。
对于有显式的构造和析构函数的类,不要建立全局的实体,因为你不知道它们在何时创建和消除。
仅在必要时,才使用“类”。
2、class vs.struct
如果只是数据的封装,用struct即可。
3、公共/保护/私有成员( public. protected和private )
按照这样的次序来说明类中的成员: public、 protected. private。
4、数据成员
数据类型的成员用m_ name说明。
不要使用公共的数据成员,要用inline访问函数,这样可兼顾封装和效率。
5、虚函数 ( Virtual Function )
使用虚函数来实现多态( Polymorphism)。
仅在很有必要时,才使用虚函数。
如果一个类型要实现多态,在基类( Base Class )中的析构函数应该是虚函数。
6、构造函数( Constructors )
不要在构造函数中做复杂的操作,简单初始化所有数据成员即可。
构造函数不应该返回错误(事实上也无法返回)。把可能出错的操作放到HrInit()或FInit()中。
7、析构函数 ( Destructor )
把所有的清理工作都放在析构函数中。如果有些资源在析构函数之前就释放了,记住要重置这些成员为0或NULL。
析构函数也不应该出错。
8、new 和delete
如果可能,实现自已的new/delete,这样可以方便地加上自已的跟踪和管理机制。自已的new/delete可以包装系统提供的new/delete。
检查new的返回值。new 不一定都成功。
释放指针时不用检查NULL。
9、运算符 ( Operators )
在理想状态下,我们定义的类不需要自定义操作符。确有必要时,才会自定义操作符。
运算符不要做标准语义之外的任何动作。例如.“==”的判断不能改变被比较实体的状态。
运算符的实现必须非常有效率,如果有复杂的操作,应定义一个单独的函数。
当你拿不定主意的时候,用成员函数.不要用运算符。
10、异常( Exceptions )
异常是在“异乎寻常”的情况下出现的,它的设置和处理都要花费“异乎寻常”的开销,所以不要用异常作为逻辑控制来处理程序的主要流程。
了解异常及处理异常的花销,在C++语言中,这是不可忽视的开销。
当使用异常时,要注意在什么地方清理数据。
异常不能跨过DLL或进程的边界来传递信息,所以异常不是万能的。
11、类型继承( Class Inheritance )
仅在必要时,才使用类型继承。
用const标注只读的参数(参数指向的数据是只读的,而不是参数本身)。
用const标注不改变数据的函数。 -
代码复审:看按代码是否在代码规范的框架内正确地解决了问题。
名称 | 形式 | 目的 |
---|---|---|
自我复审 | 自己vs.自己 | 用同伴复审的标准来要求自己。不一定最有效,因为开发者对自己总是过于自信。如果能持之以恒,则对个人有很大好处 |
同伴复审 | 复审者 vs. 开发者 | 简便易行 |
团队复审 | 团队 vs. 开发者 | 有比较严格的规定和流程,用于关键的代码,以及复审后不再更新的代码。 覆盖率高——有很多双眼睛盯着程序。但是有可能效率不高(全体人员都要到会) |
- 代码复审的目的在于:
1、找出代码的错误。
2、发现逻辑错误, 程序可以编译通过,但是代码的逻辑是错的。
3、发现算法错误, 比如使用的算法不够优化,边界条件没有处理好等。
4、发现潜在的错误 和回归性错误——当前的修改导致以前修复的缺陷又重新出现
5、发现可能需要改进的地方
6、教育(互相教育)开发人员,传授经验,让更多的成员熟悉项目各部分的代码,同时熟悉和应用领域相关的实际知识 - 结对编程概念:
是一种敏捷软件开发的方法,两个程序员在一个计算机上共同工作。一个人输入代码,而另一个人审查他输入的每一行代码。输入代码的人称作驾驶员,审查代码的人称作观察员(或导航员)。两个程序员经常互换角色。 和现实生活中的例子类似,一个人负责具体的执行(驾驶,用键盘编写程序等),另一人负责导航、检查、掩护等。
任务二:两两自由结对,对结对方《实验二 软件工程个人项目》的项目成果进行评价,具体要求如下:
(1)对项目博文作业进行阅读并进行评论,评论要点包括:博文结构、博文内容、博文结构与PSP中“任务内容”列的关系、PSP中“计划共完成需要的时间”与“实际完成需要的时间”两列数据的差异化分析与原因探究,将以上评论内容发布到博客评论区。
项目 | 内容 |
---|---|
结对方博客评论链接 | 201971010160-谢家俊 |
结对方Github项目仓库链接 | Github |
(2)克隆结对方项目源码到本地机器,阅读并测试运行代码,参照《现代软件工程—构建之法》4.4.3节核查表复审同伴项目代码并记录。
(3)依据复审结果尝试利用github的Fork、Clone、Push、Pull request、Merge pull request等操作对同伴个人项目仓库的源码进行合作修改。
- 克隆结对方项目源码到本地机器并测试运行代码
- 代码核查表
项目部分 | 完成情况 |
---|---|
概要部分 | |
代码能符合需求和规格说明么? | 代码完成了部分需求,代码编写基本规范 |
代码设计是否有周全的考虑? | 是 |
代码的可读性? | 简单易读 |
代码是否容易维护? | 易维护 |
代码的每一行是否都执行并检查过? | 是 |
设计规范部分 | |
设计是否遵从已知的设计模式或项目中常用的模式? | 是 |
有没有硬编码或字符串/数字等存在? | 没有 |
代码有没有依赖于某一平台,是否会影响将来的移植? | 否 |
开发者新写的代码是否用已有的Library/SDK/Framework中的功能实现?在本项目中是否存在类似的功能可以通过调用而不用全部重新实现? | 否 |
有没有无用的代码可以清除? | 否 |
代码规范部分 | |
修改的部分符合代码标准和风格么? | 符合 |
具体代码部分 | |
有没有对错误进行处理?对于调用的外部函数,是否检查了返回值或处理了异常? | 已处理 |
参数传递有无错误,字符串的长度是字节的长度还是字符的长度,是从0开始计数还是从1开始计数 | 无错误 |
边界条件是如何处理的?switch语句和default分支是如何处理的?循环有没有可能出现死循环? | 没有出现死循环 |
有没有使用断言来保证我们认为不变的条件真的得到满足? | 否 |
数据结构中有没有用不到的元素? | 否 |
效能 | |
代码的效能如何?最坏的情况是怎么样的? | 效能一般 |
代码中,特别是循环中是否有明显可优化的部分? | 有 |
对于系统和网络的调用是否会超时?如何处理? | 否 |
可读性 | |
代码可读性如何?有没有足够的注释? | 代码有较好的可读性 |
可测试性 | |
代码是否需要更新或创建新的单元测试? | 否 |
任务三: 采用两人结对编程方式,设计开发一款{0-1}KP 实例数据集算法实验平台,使之具有以下功能:
1、基本功能设计
- 平台基础功能:实验二 任务3;
- 可正确读入实验数据文件的有效{0-1}KP数据;
- 能够绘制任意一组{0-1}KP数据以价值重量为横轴、价值为纵轴的数据散点图;
- 能够对一组{0-1}KP数据按重量比进行非递增排序;
- 用户能够自主选择贪心算法、动态规划算法、回溯算法求解指定{0-1} KP数据的最优解和求解时间(以秒为单位);
- 任意一组{0-1} KP数据的最优解、求解时间和解向量可保存为txt文件或导出EXCEL文件。
- {0-1}KP 实例数据集需存储在数据库;
- 平台可动态嵌入任何一个有效的{0-1}KP 实例求解算法,并保存算法实验日志数据;
- 人机交互界面要求为GUI界面(WEB页面、APP页面都可);
- 查阅资料,设计遗传算法求解{0-1}KP,并利用此算法测试要求(3);
2、需求分析
D{0-1}KP问题可以采用动态规划算法,回溯算法以及遗传算法等多种算法来解决,每一种算法解决D{0-1}KP问题所消耗的时间和空间都有所不同,为了方便用户快速的选择某种算法来解决D{0-1}KP问题并且比较每一种算法执行时所消耗的时间和空间,所以我们试图开发一个D{0-1}KP实例数据集算法实验平台,以便用户能够快速的选择某种算法来解决D{0-1}KP问题并且比较出哪种算法更高效。
3、软件设计说明
- 在实验二-个人项目的基础上进行开发;
- 人机交互界面通过python来编写GUI界面;
- 将D{0-1}KP实例数据集存储在数据库,在GUI界面可进行数据的查询;
- 输入需要绘制散点图或者需要排序的数据集以及数据项后进行散点图的绘制或者数据的排序;
- 平台动态嵌入有效的{0-1}KP实例求解算法。
4、程序运行图
-
数据库
-
GUI界面设计
-
散点图
-
排序
-
算法模块
5、核心代码
- 散点图绘制
def paint(list11=[],list22=[]):
import numpy as np
import matplotlib.pyplot as plt
plt.scatter(list11,list22)
plt.show()
- 数据排序
def sort(list11=[],list22=[]):
win1 = tkinter.Toplevel()
win1.title('数据排序')
win1.geometry('500x300')
sw = win1.winfo_screenwidth()
sh = win1.winfo_screenheight()
win1.geometry('+%d+%d' % ((sw - 500) / 2, (sh - 300) / 2))
list4=[]
for i in range(2,len(list11)+1):
if i%3==0:
list4.append(round(int(list11[i-1])/int(list22[i-1]),3))
list4.sort(reverse=True)
tkinter.messagebox.showinfo("按照性价比的非递增排序",list4)
win1.destroy()
- GUI界面布局
win = tkinter.Tk()
win.title('D{0-1}背包问题')
win.geometry('500x300')
win.configure(bg='Linen')
sw = win.winfo_screenwidth()
sh = win.winfo_screenheight()
win.geometry('+%d+%d' % ((sw - 500) / 2, (sh - 300) / 2))
l = tkinter.Label(win, text='{0-1}KP实例数据集算法实验平台', font=('华文行楷', 24), fg='black')
l.place(relx=0.5, rely=0.1, anchor='center')
Lname = tkinter.Label(win, text='数据集文件名', font=('华文行楷', 19), fg='black')
Lname.place(relx=0.45, rely=0.3, anchor='center')
nu = tkinter.StringVar()
name = tkinter.Entry(win, textvariable=nu)
name.place(relx=0.51, rely=0.29, width=95)
choose = [('1.进行数据的查询', 1), ('2.绘制数据散点图', 2), ('3.对数据进行排序', 3), ('4.选择算法来求解', 4)]
v = tkinter.IntVar()
v.set(1)
x, y = 0.5, 0.5,
for a, b in choose:
cc = tkinter.Radiobutton(win, text=a, variable=v, value=b, font=('华文行楷', 20), fg='black')
cc.place(relx=x, rely=y, anchor='center')
y += 0.1
b = tkinter.Button(win, text='确定', width=10, height=3, bg='gray', command=secondMain)
b.place(relx=0, rely=1, anchor='sw')
b2 = tkinter.Button(win, text='退出', width=10, height=3, bg='gray', command=win.quit)
b2.place(relx=1, rely=1, anchor='se')
6、展示psp
PSP2.1 | 任务内容 | 计划共完成需要的时间(min) | 实际完成需要的时间(min) |
---|---|---|---|
Planning | 计划 | 30 | 25 |
Estimate | 估计这个任务需要多少时间,并规划大致工作步骤 | 30 | 25 |
Development | 开发 | 1120 | 953 |
Analysis | 需求分析(包括学习新的技术) | 310 | 260 |
Design Spec | 生成设计文档 | 90 | 60 |
Design Review | 设计复审 | 30 | 25 |
Coding Standard | 代码规范(为开发制定合适的规范) | 10 | 8 |
Design | 具体设计 | 60 | 40 |
Coding | 具体编码 | 500 | 520 |
Code Review | 代码复审 | 60 | 40 |
Test | 测试(自我测试,修改代码,提交修改) | 60 | 30 |
Reporting | 报告 | 100 | 70 |
Test Report | 测试报告 | 60 | 30 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结 ,并提出过程改进计划 | 30 | 30 |
7、实验总结描述结对的过程,提供两人在讨论、细化和编程时的结对照片(非摆拍)
8、实验总结
- 本次实验采用了结对编程的思想以及做法,通过本次实验我觉得结对编程有它的好处也有不好的地方。首先,在设计以及编码的过程中两个人的思路是不同的,尤其在编码过程中,很好的避免了因为一个小问题而去钻牛角尖的问题,同时在设计方面也不再很单调,两个人的想法会多一点,对题目的理解也会有所不同,能让我们更好的理解和完成实验。但是结对编程需要两个人不断地去讨论,很多问题面对面的解决会更有效,但是两人的时间会有一定的差异,这样有些问题就需要通过社交媒体来交流,这样不利于解决问题,有时对一个问题还会产生理解上的偏差,容易在编码时出现错误。但总的来说,结对编程是利大于弊的,现在出现的问题很大程度上是所处环境造成的,但帮助是很大的,通过本次实验,我从结对伙伴身上学到了很多值得学习的品质与精神。
任务四: 完成结对项目报告博文作业
- 已完成博文作业