软件工程实践2019第五次作业——结对编程的编程实现:
1.博客链接及Github项目地址:
作业的博客链接:https://edu.cnblogs.com/campus/fzu/2019FZUSEZ/homework/8736
Github项目地址:https://github.com/darkness-li/031702429-031702430
结对同学博客链接:
学号 | 姓名 | 博客链接 |
---|---|---|
031702429 | 陈功贤 | https://www.cnblogs.com/gs-23/ |
031702430 | 陈友昆 | https://www.cnblogs.com/chenyoukun/ |
2.具体分工:
031702429陈功贤:主要负责UI设计、素材收集、归纳总结
031702430陈友昆:主要负责编写代码、归纳总结、测试
3.PSP表格:
PSP2.1 | Personal Software Process Stages | 预估耗时(小时) | 实际耗时(小时) |
---|---|---|---|
Planning | 计划 | 0.5 | 0.5 |
Estimate | 估计这个任务需要多少时间 | 0.5 | 0.5 |
Development | 开发 | 4.5 | 6 |
Analysis | 需求分析 (包括学习新技术) | 25 | 30 |
Design Spec | 生成设计文档 | 2 | 2 |
Design Review | 设计复审 | 2 | 1.5 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 2 | 1.5 |
Design | 具体设计 | 2 | 2.5 |
Coding | 具体编码 | 15 | 18 |
Code Review | 代码复审 | 1 | 1.5 |
Test | 测试(自我测试,修改代码,提交修改) | 5 | 6 |
Reporting | 报告 | 2 | 3 |
Test Repor | 测试报告 | 1 | 1.5 |
Size Measurement | 计算工作量 | 1 | 0.5 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 2 | 1.5 |
合计 | 60.5 | 76.5 |
4.解题思路描述与设计实现说明:
解题思路:
分析题目的需求我们可以知道大致的主要实现过程是这样的:首先我们需要在web页面提供一个文本框,然后在文本框中输入给定的师生信息,接着把师生信息以树形结构的形式展现出来。因此可知我们可以初步得到代码的组织与内部实现设计如下图所示:
关键算法及其代码实现:
1.创建支持多行输入的文本框以及点击按钮:
主要代码如下:
说明:extarea标签用来定义多行的文本输入控件,其中的文本区可以容纳无限数量的文本,可以通过 cols 和 rows 属性来规定 textarea 的尺寸;button标签用来定义一个按钮,在 button 元素的内部,可以放置内容,onclick的属性由元素上的鼠标点击触发;
2.处理文本框中输入的数据:
数据的输入处理考虑的是关键信息的提取。第一,我们要提取出每一个人的身份标签,如导师、2016级博士生、2017级本科生等;第二,我们要提取出导师和学生的名字信息,如张三、天一、吴五等,对输入文本的切分以及关键信息的提取是我们后面正确构造出树形结构的师门树的关键所在。
主要代码如下:
function getdata() { var text = $("#text").val(); //获取id为text的textarea的全部内容 var text1 = text.split("\n\n");//针对多组数据输入的情况,以“\n\n"为关键字进行分组,调用split函数进行分割 for (var k = 0; k < text1.length; k++) { //text1.length用于得到分组的数量 var arry = text1[k].split("\n"); //针对每一组数据,以“\n"为关键字进行分组,得到每条导师和学生的信息 for (var ii = 0; ii < arry.length; ii++) { var newarr = arry[ii].split(":");//针对每条导师和学生的信息,以“:”为关键字进行分组,可得到身份标签和身份信息 var a1 = newarr[0]; //获取身份标签,如导师、2016级博士生等,保存在a1变量 alert(a1); //alert函数用于在web页面上显示变量的值,当前用于显示身份标签 var a2 = newarr[1]; //获取身份信息,如天一、王二、吴五等,保存在a2变量 var a3 = a2.split("、");//针对每组身份信息,以“、”为关键字进行切分,得到每个人的名字信息 for (var j = 0; j < a3.length; j++) { //a3.length用于得到每条身份信息里名字的数量 alert(a3[j]); //显示每个导师或学生的名字 } } } }
说明:首先,我们需要获取文本域中的信息内容,在这里可以利用.val()方法来处理表单元素的值,用它来获取id为text的文本域中的全部内容,并存储在text变量中。接着,就是根据关键字的标志信息来切分文本内容,例如多组师生信息的输入是以“\n\n"为标志来切分成单组的,而身份标签和名字信息是以”:“为标志进行切分,多个名字之间按照”、“标志进行切分,在切分之后,将相应的信息存储到相应的变量中,详细实现过程可以参考上面的代码注释。
3.将处理好的数据以树形结构呈现:
这个算法的实现确实超出了我和队友的能力,我们不仅要将处理好的信息以树形结构的形式组织展现,并且还要实现节点的缩放功能,尽管初步学习了有关前端的一些知识我们还是感到无从入手。最后,在网上查阅了相关的实现方法之后,我们决定利用d3.js库来实现树形结构,d3.js是一个JavaScript库,用于根据数据来处理文档,使用它主要是用来做数据可视化的。有关d3的学习我们参考了d3官网http://d3js.org/和网上的教程http://www.it1352.com/OnLineTutorial/d3js/index.html
主要代码如下:
首先我们需要通过链接的方式导入d3.js文件:
以下是生成树形结构和实现节点缩放功能的主要代码:
function maketree(k) { var margin = { top: 20, right: 120, bottom: 20, left: 120 }, width = 960 - margin.right - margin.left, height = 500 - margin.top - margin.bottom; var i = 0, duration = 750, //过渡延迟时间 root; var tree = d3.layout.tree() //创建一个树布局 .size([height, width]); var diagonal = d3.svg.diagonal() .projection(function(d) { return [d.y, d.x]; }); //创建新的斜线生成器 //声明与定义画布属性 var svg = d3.select("body").append("svg") .attr("width", width + margin.right + margin.left) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); root = treeData[k]; //treeData为上边定义的节点属性 root.x0 = height / 2; root.y0 = 0; update(root); function update(source) { // Compute the new tree layout.计算新树图的布局 var nodes = tree.nodes(root).reverse(), links = tree.links(nodes); // Normalize for fixed-depth.设置y坐标点,每层占180px nodes.forEach(function(d) { d.y = d.depth * 180; }); // Update the nodes…每个node对应一个group var node = svg.selectAll("g.node") .data(nodes, function(d) { return d.id || (d.id = ++i); }); //data():绑定一个数组到选择集上,数组的各项值分别与选择集的各元素绑定 // Enter any new nodes at the parent's previous position.新增节点数据集,设置位置 var nodeEnter = node.enter().append("g") //在 svg 中添加一个g,g是 svg 中的一个属性,是group的意思,它表示一组什么东西,如一组lines,rects ,circles其实坐标轴就是由这些东西构成的 .attr("class", "node") //attr设置html属性,style设置css属性 .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; }) .on("click", click); nodeEnter.append("rect") .attr("x", -23) .attr("y", -10) .attr("width", 70) .attr("height", 20) .attr("rx", 10) .style("fill", "#800000"); //d 代表数据,也就是与某元素绑定的数据。 nodeEnter.append("text") .attr("x", function(d) { return d.children || d._children ? 13 : 13; }) .attr("dy", "10") .attr("text-anchor", "middle") .text(function(d) { return d.name; }) .style("fill", "white") .style("fill-opacity", 1); var nodeUpdate = node.transition() //开始一个动画过渡 .duration(duration) //过渡延迟时间,此处主要设置的是圆圈节点随斜线的过渡延迟 .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); nodeUpdate.select("rect") .attr("x", -23) .attr("y", -10) .attr("width", 70) .attr("height", 20) .attr("rx", 10) .style("fill", "#800000"); nodeUpdate.select("text") .attr("text-anchor", "middle") .style("fill-opacity", 1); // Transition exiting nodes to the parent's new position.过渡现有的节点到父母的新位置。 //最后处理消失的数据,添加消失动画 var nodeExit = node.exit().transition() .duration(duration) .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; }) .remove(); nodeExit.select("circle") .attr("r", 1e-6); nodeExit.select("text") .attr("text-anchor", "middle") .style("fill-opacity", 1e-6); // Update the links…线操作相关 //再处理连线集合 var link = svg.selectAll("path.link") .data(links, function(d) { return d.target.id; }); // Enter any new links at the parent's previous position. //添加新的连线 link.enter().insert("path", "g") .attr("class", "link") .attr("d", function(d) { var o = { x: source.x0, y: source.y0 }; return diagonal({ source: o, target: o }); //diagonal - 生成一个二维贝塞尔连接器, 用于节点连接图. }) .attr('marker-end', 'url(#arrow)'); // Transition links to their new position.将斜线过渡到新的位置 //保留的连线添加过渡动画 link.transition() .duration(duration) .attr("d", diagonal); // Transition exiting nodes to the parent's new position.过渡现有的斜线到父母的新位置。 //消失的连线添加过渡动画 link.exit().transition() .duration(duration) .attr("d", function(d) { var o = { x: source.x, y: source.y }; return diagonal({ source: o, target: o }); }) .remove(); // Stash the old positions for transition.将旧的斜线过渡效果隐藏 nodes.forEach(function(d) { d.x0 = d.x; d.y0 = d.y; }); } //定义一个将某节点折叠的函数 // Toggle children on click.切换子节点事件 function click(d) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } update(d); } }
5.附加特点设计与展示:
设计的创意独到之处,这个设计的意义:
1.在网页上我们能够支持多棵树并存的形式,但由于能力有限,暂时还没办法完成关联树的实现;
2.我们在页面文本框的底部添加了一个刷新按钮,考虑到用户可能输错数据或者在实现一组家族树后想要继续输入数据的需求,可以比较快捷地进行更改;
3.我们在页面上插入了一些背景图片来进行美化界面;
实现思路:
在网上学习d3.js库和一些前端知识来一步步实现
有价值的代码片段:
实现刷新功能:
利用css选择器来插入背景图片以及美化字体和文本框:
body {
margin: 0px;
padding: 0px;
background-image: url('images/timg.jpg');
background-size: 100%;
background-repeat: repeat;
background-size: cover;
text-align: center;
}
p {
margin: 0;
padding: 0;
}
.flex-container {
background-size: 100% 100%;
background-attachment: fixed;
}
.center {
display: block;
margin-left: auto;
margin-right: auto;
margin-top: auto;
margin-bottom: auto;
opacity: 0.4;
}
.button_left {
position: absolute;
left: 410px;
width: 100px;
padding: 4px;
opacity: 0.4;
}
.button_right {
position: absolute;
right: 410px;
width: 100px;
padding: 5px;
opacity: 0.4;
}
.node {
cursor: auto;
}
.node circle {
fill: rgb(174, 53, 164);
stroke: rgb(224, 124, 9);
stroke-width: 3px;
}
.node rect {
fill: #800000;
stroke: #800000;
stroke-width: 5px;
}
.node text {
font: 12px "楷体", sans-serif;
}
.link {
fill: none;
stroke: rgb(28, 235, 166);
stroke-width: 2px;
}
#title {
text-align: center;
color: rgb(73, 26, 245);
font: 30px "楷体", sans-serif;
font-weight: normal;
line-height: 70px;
text-shadow: rgb(34, 34, 34) 2px 2px 3px;
background: rgb(243, 111, 49);
}
实现成果展示:
1.初始界面:
2.生成单棵师门树:
生成的师门树:
3.多棵师门树共存:
6.在博客中给出目录说明和使用说明:
目录结构:
目录说明:
- 家族树
- bootstrap:bootstrap框架文件
- css:网页的css样式文件
- images:插入的图片
- js:html文件中需要导入的一些js文件
- type:html中需导入的css文件
- 学术家族树:html文件
- README:目录说明与使用说明文件
使用说明:
- 点击github上的“Clone or download”按钮后,将上述文件打包下载,解压后即可使用,需保证上述所有文件在同一个文件夹下。
- 使用时直接用Chrome打开学术家族树.html文件即可,输入格式按照作业要求即可。
- 在出现的文本框输入数据,点击生成家族树按钮,将会在下方生成一棵以导师为根节点的树(暂不支持关联树的实现...),然后点击刷新按钮可以刷新页面重新输入。
7.单元测试:
1.测试工具:Mocha
测试工具的学习网站:http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html
8.Github的代码签入记录:
9.遇到的代码模块异常或结对困难及解决方法:
(1)问题描述:针对多组师生信息的输入,不知道以什么为关键字为标志信息,一直无法正确进行切分,提取出每组的师生信息
做过哪些尝试:上网百度、询问同学
是否解决:已经解决
收获:提升了自己分析问题和解决问题的能力,并且对JavaScript的认识有了进一步的提高。
(2)问题描述:在实现了生成单棵师门树和多棵师门树共存的基础上,一直没办法呈现两颗关联树共存的形式(自闭....)
做过哪些尝试:不断地修改代码、去网上搜索相关信息
是否解决:未解决
收获:虽然没办法呈现两颗关联树共存的形式,但是在不断的修改代码和去网上学习相关知识的过程中,既锻炼了自己也学到了很多新知识
(3)问题描述:在界面设计时想做两个界面,输入内容一个界面,生成的家族树一个界面,不过一直无法实现,页面跳转后无法生成家族树
做过哪些尝试:百度,查看一些教程
是否解决:未解决
(4)问题描述:用mocha进行单元测试时,不会编写测试脚本。。。
做过哪些尝试:查看网上的一些教程视频,自己进行脚本编写
是否解决:已解决
10.对队友的评价:
陈友昆:我的队友是一个很主动积极的人,这次的结对编程作业涉及很多前端的知识,这是我们两个人之前完全没有接触过的。他在这几天中花了很多时间去学习HTML、css、JavaScript等前端设计的基础知识和教程,并且在组队的过程中我们配合地也比较默契,分工明确,即使有一些分歧的时候他也能够很耐心地和我去磨合,他是一个很不错的队友,希望以后有机会我们还能再见一次凌晨四点的福大!
陈功贤:我的队友是一个认真努力的人,他的代码能力很强,这次多亏他的全面帮助,我们的数据处理才能有效的实现,结对项目才能如此顺利,通过这次结对,从队友身上学到了很多。这次结对训练完成以后还要继续加强学习才好,不停地求索进步。个人觉得我们两个在结对配合和交流上相对比较顺畅,总能适时地交流意见和建议,他是一个很不错的队友,希望以后有机会我们还能再见一次凌晨四点的福大!