小学生四则运算系统项目总结
在经历了六周的时间,经历了需求分析,系统设计,编码调试以及测试,我们共同完成了小学生四则运算系统,实现了基本功能。在这个过程中我们真正走过了一遍软件开发的流程。现在对项目做一个总结,讲解项目的功能实现过程,并谈谈大家对这个课程和项目的感受~
①小组分工
这次我们的小组开发团队属于小团队,软件设计、开发的过程中需要频繁的交流,最开始的时候大家做了大致的小组分工,后期根据个人的不同情况和项目开发状态,还经历了多次调整。其实在过程中,多数的设计开发过程,大家都是在一起进行的,属于功能团队模型,但每个人都有自己的侧重点,这样能最好的发挥各自的优势,并能很好的合作。晓丽同学主要负责前期的调查问卷的设计,发放和调查问卷结果的分析,让项目的功能和UI设计方向大致确定。雪莹同学负责后台的代码与前后台的接口部分,基本实现了系统的功能。功功同学负责前端的设计,依据自己绝佳的审美和设计让系统有了比较美观的界面。旭莹同学负责后期的代码测试,文档整理,使项目更加完整。
图1 github上commit图
图2 network图
这次我们的项目托管在github上,所有文档及代码都上传到github中,一开始我们制定的上传方式是每个人一个branch,在自己的branch上工作,当工作都完成时,合并并删除分支。但在项目进行中,发现这种方法的弊端是,当我需要另一个分支的文件的时候,查看不方便,于是我们修改了上传的规则。每push一次文件,就要提出一个pull request ,并通知其他组员,comment这个pull request。这样再合并分支。这样大家都知道了项目的进度,查找文件也更方便了。这时其中一次pull request的截图。
图3 pull request
②项目设计
在项目设计的时候我们对项目的功能和UI设计做了详细的调查,对用户的需求分析做了详细的调查。我们主要分成了两部分,一个部分是在线上利用微信平台对认识的亲戚朋友进行采访。另一个部分是利用网上调查问卷的形式对软件的功能、设计进行了调查,最后对143份问卷进行了分析。具体细节在之前的博客已经展示了,需要查看的点下面的链接~
http://www.cnblogs.com/hahalovejava20160905/p/5941030.html
③项目实现
系统采用网站的形式,开发工具为Visual Studio 2013和SQL server 2008。使用的是C#.net作为后台,前台使用的是HTML+CSS+JavaScript。具体实现了登录,注册,随机测试,错题再测,查看测试记录的功能。下面就具体功能详细说一下项目的实现。
1.用户管理功能
图4 登录界面
登录界面是系统的起始页,不登录就没法进入系统,运用前台JavaScript的代码,保证了登录的窗口的自适应,就是不论窗口多大,输入用户名,密码的模块始终在窗口中展示。下图就是窗口缩小后的状态。
图5 窗口自适应
注册功能分为三步,输入信息,确认信息,注册成功,其中输入信息的时候,系统可以自主判断用户名是否重复,保证用户的用户名是不能重复的。
图6 注册界面
如果在登录界面忘记了密码,可以进入找回密码的部分,利用之前注册填写的找回密码的问题和答案来重置密码。
图7 找回密码
图8 重置密码
在进入系统后任意一个个人中心的按钮,可以进入个人中心的部分查看自己的信息,可以选择退出系统或者修改自己的个人信息。
图9 个人中心
2.测试功能
登录系统后进入到系统的主页面,在主页面我们一开始设计的有四种测试的功能。现在由于时间的问题,只做了前两种测试,后两种测试还有待完善。
图10 系统主页面
首先说一下随机测试,随机测试可以自己设置随机测试的相关参数。
图11 选择参数
对于分数的显示使用了MathJax。
在<head>标签里加上以下代码就可以实现分数表示,但注意一定要联网。
1 <script type="text/x-mathjax-config"> 2 MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}}); 3 </script> 4 <script type="text/javascript" 5 src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> 6 </script> 7 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
随机出题并结题的步骤主要有以下三个步骤,第一是利用程序的随机函数生成算式,并存到List<string>中,其中,有检查算式是否重复的函数。具体代码如下:
1 public static int ran(int down, int up) //随机生成1至n的整数 2 { 3 //RandomNumbers.Seed(); 4 5 int result = r.Next(down, up+1);//(RandomNumbers.NextNumber() % (up - down + 1) + 1); 6 return result; 7 } 8 9 public static bool is_unique(string str, List<string> s) //判断生成的算式是否独特 10 { 11 int count = 0; 12 for (int i = 0; i < s.Count; i++) 13 { 14 if (str != s[i]) 15 { 16 count++; 17 } 18 else 19 break; 20 } 21 if (count == s.Count) 22 { 23 return true; //生成的算式是独特的 24 } 25 else 26 { 27 return false; 28 } 29 } 30 31 public List<string> generate(int low, int high, int muldiv, int frac, int par) 32 { 33 int integer1; 34 int integer2; 35 List<string> str = new List<string>(); //str用来保存生成的题目 36 int ch1; //switch语句的选项 37 int ch2; 38 39 int ch4; 40 char sign = ' '; //运算符号 41 //int right1 = 0; 42 //int wrong = 0; 43 for (int i = 1; i <= 20; ) 44 { 45 int start = 1; //start用来标记算式是否是刚开始生成 46 string first = null; //四则运算的第一个运算数,第二个运算数 47 string second = null; 48 string cal = null; 49 int num = ran(1, 6) + 1; //num为参与运算的参数个数 50 for (int j = 1; j < num; ) 51 { 52 //------------------------------------------------------- 53 if (muldiv == 1) //允许乘除 54 { 55 ch1 = ran(1, 3); //随机生成运算符号 56 switch (ch1) 57 { 58 case 1: 59 sign = '+'; 60 break; 61 case 2: 62 sign = '-'; 63 break; 64 case 3: 65 sign = '*'; 66 break; 67 case 4: 68 sign = '/'; 69 break; 70 default: 71 Console.Write("Have bug!"); 72 Console.Write("\n"); 73 break; 74 } 75 } 76 else //不允许乘除 77 { 78 ch1 = ran(1, 2); 79 switch (ch1) 80 { 81 case 1: 82 sign = '+'; 83 break; 84 case 2: 85 sign = '-'; 86 break; 87 default: 88 Console.Write("Have bug!"); 89 Console.Write("\n"); 90 break; 91 } 92 } 93 //------------------------------------------------------- 94 if (frac == 1) //算式中有分数 95 { 96 ch2 = ran(1, 3); //四则运算题目的三种情况 97 switch (ch2) 98 { 99 case 1: //整数和整数 100 { 101 102 integer1 = ran(low, high); 103 integer2 = ran(low, high); 104 105 first = Convert.ToString(integer1); 106 second = Convert.ToString(integer2); 107 } 108 break; 109 case 2: //整数和分数 110 { 111 112 integer1 = ran(low, high); 113 114 first = Convert.ToString(integer1); 115 Fraction f = reduction(reafra(low, high)); 116 second = '[' + f.high + "\\" + f.low + ']'; 117 } 118 break; 119 case 3: //分数和分数 120 { 121 Fraction f1 = reduction(reafra(low, high)); 122 Fraction f2 = reduction(reafra(low, high)); 123 first = "[" + f1.high + "\\" + f1.low + "]"; 124 second = "[" + f2.high + "\\" + f2.low + "]"; 125 } 126 break; 127 default: 128 Console.Write("有错误!"); 129 Console.Write("\n"); 130 break; 131 } 132 } 133 else //不允许真分数参与运算 134 { 135 136 integer1 = ran(low, high); 137 138 first = Convert.ToString(integer1); 139 integer2 = ran(low, high); 140 141 second = Convert.ToString(integer2); 142 } 143 //------------------------------------------------------- 144 if (par == 1) //允许带括号 145 { 146 ch2 = ran(1, 4); 147 switch (ch2) 148 { 149 case 1: 150 { 151 if (start == 1) //start为1表示算式还未生成前两个运算数 152 { 153 cal = first + sign + second; 154 start = 0; 155 } 156 else 157 { 158 cal = cal + sign + first; //将已经生成的算式和新生成的运算数相连 159 } 160 } 161 break; 162 case 2: 163 { 164 if (start == 1) 165 { 166 cal = second + sign + first; 167 start = 0; 168 } 169 else 170 { 171 cal = second + sign + cal; 172 } 173 } 174 break; 175 case 3: 176 { 177 if (start == 1) 178 { 179 cal = '(' + first + sign + second + ')'; //添加括号的情况 180 start = 0; 181 } 182 else 183 { 184 cal = '(' + cal + sign + first + ')'; 185 } 186 } 187 break; 188 case 4: 189 { 190 if (start == 1) 191 { 192 cal = '(' + second + sign + first + ')'; 193 start = 0; 194 } 195 else 196 { 197 cal = '(' + second + sign + cal + ')'; 198 } 199 } 200 break; 201 default: 202 Console.Write("Have bug!"); 203 Console.Write("\n"); 204 break; 205 } 206 } 207 else //不允许括号参与运算 208 { 209 ch4 = ran(1, 2); //输出的两种情况 210 switch (ch4) 211 { 212 case 1: 213 { 214 if (start == 1) 215 { 216 cal = first + sign + second; 217 start = 0; 218 } 219 else 220 { 221 cal = cal + sign + first; 222 } 223 } 224 break; 225 case 2: 226 { 227 if (start == 1) 228 { 229 cal = second + sign + first; 230 start = 0; 231 } 232 else 233 { 234 cal = second + sign + cal; 235 } 236 } 237 break; 238 default: 239 Console.Write("有错误!"); 240 Console.Write("\n"); 241 break; 242 } 243 } 244 j++; 245 } 246 if (str.Count == 0) //vector str,若str为空,添加第一个运算式到vector中 247 { 248 str.Add(cal); 249 i++; 250 } 251 if (is_unique(cal, str)) //判断生成的算式和之前已经生成的运算式是否重复 252 { 253 str.Add(cal); //将生成的运算式添加到str中 254 i++; 255 } 256 } 257 return str; 258
第二步是将生成的算式由中缀表达式转换为后缀表达式,方便后来的计算。用了栈的数据结构。
1 public class AnonymousClass 2 { 3 public char[] data = new char[DefineConstantsConsoleApplication1.maxsize]; //存放运算符 4 public int top; //栈顶指针 5 } //定义运算符栈 6 public class AnonymousClass2 7 { 8 public Fraction[] data = new Fraction[DefineConstantsConsoleApplication1.maxsize]; //存放数值 9 public int top; //栈顶指针 10 } 11 public static AnonymousClass op = new AnonymousClass(); 12 13 public static void trans(string exp, char[] postexp) //exp[]为中缀表达式,postexp[]为后缀表达式 14 { 15 exp += '\0'; 16 char ch; 17 int i = 0; //i作为exp的下标,j作为postexp的下标 18 int j = 0; 19 op.top = -1; 20 ch = exp[i]; 21 i++; 22 while (ch != '\0') //exp表达式结尾默认标志'\0' 23 { 24 switch (ch) 25 { 26 case '(': //判定为左括号 27 { 28 op.top++; 29 op.data[op.top] = ch; 30 } 31 break; 32 case ')': //判定为右括号,将'('之前的运算符依次出栈放到postexp中 33 { 34 while (op.data[op.top] != '(') 35 { 36 postexp[j] = op.data[op.top]; 37 j++; 38 op.top--; 39 } 40 op.top--; //将‘(’弹出 41 } 42 break; 43 case '+': //‘+’‘-’,优先级最低,直到‘(’为止 44 case '-': 45 { 46 while (op.top != -1 && op.data[op.top] != '(') 47 { 48 postexp[j] = op.data[op.top]; 49 j++; 50 op.top--; 51 } 52 op.top++; 53 op.data[op.top] = ch; 54 } 55 break; 56 case '*': 57 case '/': 58 { 59 while (op.top != -1 && op.data[op.top] != '[' && (op.data[op.top] == '*' || op.data[op.top] == '/')) 60 { 61 postexp[j] = op.data[op.top]; 62 j++; 63 op.top--; 64 } 65 op.top++; 66 op.data[op.top] = ch; 67 } 68 break; 69 case ' ': //过滤掉空格 70 break; 71 case '[': //将分数当成一个整体放到postexp中 72 { 73 while (ch != ']') 74 { 75 postexp[j] = ch; 76 j++; 77 ch = exp[i]; 78 i++; 79 } 80 postexp[j] = ch; //将‘]‘放到postexp中 81 j++; 82 } 83 break; 84 default: 85 { 86 while (ch >= '0' && ch <= '9') //判定为数字 87 { 88 postexp[j] = ch; 89 j++; 90 ch = exp[i]; 91 i++; 92 } 93 i--; 94 postexp[j] = '#'; //用#标示一个数值的结束 95 j++; 96 } 97 break; 98 } 99 ch = exp[i]; 100 i++; 101 } 102 while (op.top != -1) //此时exp扫描完成,栈不空时,出栈并放到postexp中 103 { 104 postexp[j] = op.data[op.top]; 105 j++; 106 op.top--; 107 } 108 postexp[j] = '\0'; //给postexp表达式添加结束标识 109 }
第三步是利用栈和符号的等级来计算后缀表达式的数值。
1 public static Fraction calculate(char[] postexp) //计算后缀表达式的值 2 { 3 int d; 4 char ch; 5 int i = 0; //postexp的下标 6 st.top = -1; 7 ch = postexp[i]; 8 i++; 9 while (ch != '\0') //postexp字符串未结束 10 { 11 switch (ch) 12 { 13 case '+': 14 { 15 st.data[st.top - 1] = add(st.data[st.top - 1], st.data[st.top]); 16 st.top--; 17 } 18 break; 19 case '-': 20 { 21 st.data[st.top - 1] = minus1(st.data[st.top - 1], st.data[st.top]); 22 st.top--; 23 } 24 break; 25 case '*': 26 { 27 st.data[st.top - 1] = mul(st.data[st.top - 1], st.data[st.top]); 28 st.top--; 29 } 30 break; 31 case '/': 32 { 33 st.data[st.top - 1] = div(st.data[st.top - 1], st.data[st.top]); 34 st.top--; 35 } 36 break; 37 case '[': 38 { 39 int high = 0; 40 int low = 0; 41 ch = postexp[i]; //删除’[' 42 i++; 43 while (ch != '\\') 44 { 45 high = 10 * high + ch - '0'; 46 ch = postexp[i]; 47 i++; 48 } 49 ch = postexp[i]; //删除’\' 50 i++; 51 while (ch != ']') 52 { 53 low = 10 * low + ch - '0'; 54 ch = postexp[i]; 55 i++; 56 } 57 st.top++; 58 Fraction re = gener(high, low); 59 st.data[st.top] = re; 60 } 61 break; 62 default: 63 { 64 d = 0; 65 while (ch >= '0' && ch <= '9') //将数字字符转化为对应的数值存放到d中 66 { 67 d = 10 * d + ch - '0'; 68 ch = postexp[i]; 69 i++; 70 } 71 st.top++; 72 Fraction re = gener(d, 1); 73 st.data[st.top] = re; 74 } 75 break; 76 } 77 ch = postexp[i]; 78 i++; 79 } 80 return st.data[st.top]; 81 } 82 83 84 public static string FraToString(Fraction f) 85 { 86 string result; 87 if (f.down == 1) 88 { 89 result = f.high; 90 } 91 else 92 { 93 result = "[" + f.high + "\\" + f.low + "]"; 94 } 95 return result; 96 }
这样就实现了随机出题的功能,根据调查问卷的分析,我们决定一次出20道题目让用户计算。UI设计上决定用纯色,这样可以让用户集中于题目,最后实现的截图如下:
图12 随机出题
做完题目后,系统自动计算用户答题情况并显示相应的分数,之后用户可以查看详情,查看正确答案。
图13 测试结果
再说一下错题重测。上一步随机出题后,系统将结果传入数据库中,通过Test表,CalQuestion表和关系表TestCalQuestion表,可以查看到用户的错题,并将它从数据库中随机提取出来,让用户再次测试。如果题目小于20题,就直接显示所有的错题。多于20题就随机测试。出题的界面与随机测试的相同,就不再展示了。
在网页的头部有三个标签,开始测试就直接选择测试种类,过往测试可以显示出该用户从前做过的测试,选择相应的测试可以点击进去查看那次测试的详情。
图14 过往测试
我爱学习模块是让用户们来学习的地方。有99乘法表和三个视频组成,这个由于不同电脑,所用的视频插件并不是都兼容,还有待改进。
完整的源代码已经上传到github中Ultimate_Editon压缩包中。链接如下:https://github.com/Hahalovejava/Calculate
④个人感想
Ⅰ.原旭莹
1.以小组形式开发软件,在团队合作中,及时写文档很重要,我们小组在开发过程中及时更新文档,在需求分析阶段采用问卷调查、微信询问等方式来获取需求,并做到了及时更新需求文档,及时更新博客;在测试阶段测试报告尽可能多的包含测试用例,清楚高效的文档对我们小组之间的交流合作起到了很大的作用。
2.在小组开发中,组员之间的交流对团队合作至关重要,我们小组主要采用当面交流和微信群交流的形式,做到了有问题及时沟通,有冲突及时解决,良好的团队交流让我们更好的完成了项目。
3. 学会了使用git,之前没接触过git,但是经过本次课程,熟悉掌握了git。
Ⅱ张功
这次现代软件工程令我印象最深刻的地方大概就是如何进行团队合作了。因为之前完成项目都是自己单独进行,所有的工作都是自己来完成,所以也算是第一次和他人合作完成一个项目。我的组长和组员都很棒,都能够积极地参与到项目中,从项目功能的构想,到最后项目的实现都不是只依赖一个人完成的,虽然有些时候也会在一些问题上产生分歧,但是大家能够互相讨论解决,互相支持,互相鼓励最终完成了项目。尽管最后项目呈现出来的状态未能达到我们的期望,但是完成项目的这个过程令我很难忘,在实践中体会课程教学的内容,这才是学习这门课程的真正意义吧。
Ⅲ张晓丽
首先,老师改变传统的教课方式,不是一味地给我们灌输软件工程中的相关术语、软件过程中的方法,而是让我们以小组的方式讲课、做一个小项目,通过这种方式,不仅加强了同学间的合作意识,同时让我们在完成软件项目的过程中学习软件工程的流程,体会软件工程思想。通过讲课使我们锻炼了表达能力,在准备课件的过程中,更加深入得理解了所讲章节的内容,也使组内同学的团队合作意识增强。
其次,我们使用邹欣老师的现代软件工程教材,这本教材里有很多生动的小例子,通俗易懂,增加了读书的趣味性。通过写博客的方式来记录每周的项目进展,不仅得到了老师的及时指导,还能督促我们及时跟进项目,同时还能记录自己当时解决具体问题用的方法和心得体会。最重要的是学会了使用Github进行项目管理,如果不是这门课的要求,我可能不会主动去学习Github的使用,这是我很大的一个收获。
再次,在小学生四则运算小项目中,我主要做了需求分析这部分,需求分析是软件工程中比较核心的一部分,需求做不好,后面的开发就无从下手。我们小组开始是组内先确定大概的模块,然后通过线上咨询相关人比如弟弟妹妹,孩子家长,当教师的同学等,来获取系统功能的需求以及每个年级学生学习数学运算的进度,然后通过在问卷星平台上做调查问卷的方式来获取系统的主题风格,每次做题数以及学习模块展示方式等细节的需求。通过这个小项目,理解了软件工程中的一些思想,从拿到项目到一点点实现功能,体会了软件开发的整个过程,分工合作,最后基本实现了需求分析中功能。
最后,感谢章老师给了我们展示自我锻炼自己的机会,虽然每周都在“赶作业”,但是收获特别大。感谢邹欣老师在博客上及时帮我们解答疑问,指导我们完成项目。
Ⅳ孙雪莹
这次的现代软件工程课程给我感触很大,首先这种上课的方式和我以前接受的方式不同,更强调我们的自主学习能力。刚开始的时候,我非常不适应,以前习惯了老师教什么我听什么。这次的课程老师留了作业之后,都要自己上网查找资料学习。拿git那次的作业来时,一开始特别的迷茫,但在后来借鉴网上的教程和参考他人的博客,加上自己时间操作,掌握了git的使用方法。这对以后我工作学习都是特别有帮助的。再说说博客,以前从来都是有需要的时候,上网查资料会看到别人总结好的博客,这次真的要自己写,才发现,写博客不仅是将知识和他人分享,也是一个自我学习巩固的过程,在我写博客的时候,会考虑怎么写会让人明白,如果自己没明白就没法写,所以在这个过程中,使自己的知识又巩固了。而且博客发布到网上之后,也便于自己日后回顾。像我经常在使用git的时候会忘记命令,就可以从自己写过的博客中查看。
再说说四则运算这个项目,在需求分析的时候,我们用了调查问卷和微信询问的方式,确定了系统的大致功能和设计。但我后来在做系统的时候就发现,其实我们的需求还需要再细化,同时也发现需求分析对项目来说的重要性,所以以后在做无论大项目还是小项目的时候,要更加认真的做需求分析,并细化再细化。在做系统的时候,我们四个一起讨论,一起研究,基本实现了主要功能,在成果出来的时候,还是很有成就感的。但对于这次来说反思的教训就是,我们这次的时间规划不是很好,导致后期做项目的时候时间特别紧,有的功能就要砍掉了,最开始的时间规划是十分重要的。
总的来说,这次的课程收获还是很大的,了解了软件开发的实际过程,在和老师同学交流的过程中也受益多多,提高了自主学习的能力。这对我今后的发展会很有帮助的。这次课程虽然结束了,但这只是一个开始,未来的我们会更加努力~