一、相关地址
二、 预计在程序的各个模块开发上耗费的时间(psp):
三、接口设计
四、计算模块接口的设计和实现过程。
五、计算模块接口部分的性能改进
六、计算模块部分单元测试展示
七、模块部分异常处理:
八、界面模块的设计过程:
九、界面模块与计算模块的对接:
十、结对过程:
十一、结对编程的优点和缺点。
十二、在程序的各个模块开发实际上耗费的时间(psp):
十三、总结
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
项 目 测 试 地 址: http://39.105.6.214/PairProgram2_war/
Coding.net 项目地址:https://git.coding.net/chenf640/pairProgram.git
(参考学号:2016011001 密码:123123)
(命令行测试:
1.进入src文件夹;
2.在命令行输入javac -encoding utf-8 Command.java;
3.回车再输入 java Command -n 10 -m 1 100 (或其他命令)
回车,result.txt文件创建成功
)
PSP2.1 |
任务内容 |
计划共完成需要的时间(min) |
Planning |
计划 |
1*60 |
· Estimate |
· 估计这个任务需要多少时间,并规划大致工作步骤 |
1*60 |
Development |
开发 |
50*60 |
· Analysis |
· 需求分析 (包括学习新技术) |
5*60 |
· Design Spec |
· 生成设计文档 |
2*60 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
1*60 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
1*60 |
· Design |
· 具体设计 |
3*60 |
· Coding |
· 具体编码 |
26*60 |
· Code Review |
· 代码复审 |
4*60 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
8*60 |
Reporting |
报告 |
7*60 |
· Test Report |
· 测试报告 |
5.5*60 |
· Size Measurement |
· 计算工作量 |
30 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
1*60 |
首先,虽然以前用过接口,但并不知接口到底要怎么进行设计才更合理。于是,在设计接口之前我先去了解了关于老师提出的几个英文词组。
1.Information Hiding(信息隐藏)
什么是信息隐藏:信息隐藏是指在设计和确定模块时,使得一个模块内包含的特定信息(过程或数据),对于不需要这些信息的其他模块来说,是不可访问的。
为什么要信息隐藏:
隐藏复杂度:这样你就不用再去应付它,除非你要特别关注的时候;
隐藏变化源:这样当变化发生时,其影响就能被限制在局部范围内。复杂度的根源包括复杂的数据类型、文件结构、布尔判断以及晦涩的算法等等。
信息隐藏的特点:(1)不可感知性(2)鲁棒性(反映了信息隐藏技术的抗干扰能力)(3)隐藏容量(应用于隐蔽通信中时,为了提高通信的效率)
2. Interface Design(接口设计)
接口的特点
接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
3.Loose Coupling(松耦合)
松耦合就是降低系统部件和部件之间的耦合性,也就是降低部件的依赖性,使得部件之间相对独立,这样对于日后系统的维护及扩展都是很有好处的。在J2EE中可以使用接口来降低程序的耦合度,因为多态的特性,父类或者接口的引用是可以接受子类对象的。
综上,在设计接口时,首先,我们进行了模块化设计,对一类功能的函数进行封装。其次,我们将代码量比较多,代码逻辑比较复杂的按要求产生四则运算的相关程序实行接口设计。虽然,功能程序内部很复杂,但是用户只需调用一个方法即可。只需输入相关参数,至于参数的名称、内部的实现,用户并不知道。这也为程序以后的维护和扩展提供了方便。
接口的设计和实现过程主要涉及到3个类,一个接口,10个函数和一个主函数。
Command类:包含主函数,生成四则运算模块的入口。通过该类从后台获取参数要求,并进行判断参数是否符合要求,若符合,则将通过调用接口产生符合要求的运算式。若不符合要求,则抛出异常,给出提示重新输入;
IsRight类:将从后台输入的自妇产进行解析,看是否符合生成运算式的要求,若符合,则将符合的参数返回到Command类,若不符合,则将提示参数不符合要求,重新输入;
Algorithm接口:主要包含生成带括号的运算式和不带括号的运算式两个函数,还有在生成运算式过程中会用到的判断符号优先级和将代表运算符的数字转换为相应的字符的函数(0-"+",1-"-",2-"*",3-"÷");
AlgorithmImpl类:Algorithm接口的具体实现。
类之间的关系如下:
在接口的性能改进上,我大概用了4个小时,我的思路主要就是尽量缩短生成特定要求的运算式的时间。
比如说:我以前在判断条件时,如果产生的不符合要求,则重新产生随机数,直至符合要求为止。这样有时就会一直随机产生,需要好久可能才产生一个符合要求的运算式。因此,在改进时,我主要是将不符合要求的直接舍弃,重新生成。这样会在一定程度上节约时间并且不会产生死循环。
关于性能分析图,我以前没有接触过,因此我上网搜了一下,发现关于java的性能分析工具在jdk的bin目录直接有,只需按自己的需求下载相应的插件即可。我看了一下博客,关于java VisualVM的推荐使用最多。关于这个工具的使用方法,推荐这篇博客【Java VisualVM】使用 VisualVM 进行性能分析及调优,是我看过比较详细比较详细的一篇啦。
部分截图:
关于程序中消耗最大的函数就是 生成不产生括号,运算符大于一位的符合要求的运算式 的函数。
单元测试主要分为Algorithm接口的实现AlogriyhmImpl类中的方法测试和IsRight类中的方法测试
AlgorithmTest类部分代码:
1 @Test
2 public void algorithm() throws Exception {
3 Algorithm algorithm =new AlgorithmImpl();
4 for(int i =0;i<12;i++){
5 System.out.println(algorithm.algorithm(1,100,1,1));
6 System.out.println(algorithm.algorithm(10,200,2,1));
7 System.out.println(algorithm.algorithm(5,200,4,1));
8 System.out.println(algorithm.algorithm(5,100,7,1));
9 System.out.println(algorithm.algorithm(10,200,1,2));
10 System.out.println(algorithm.algorithm(10,100,2,2));
11 System.out.println(algorithm.algorithm(1,100,4,2));
12 }
13 }
14
15 @Test
16 public void bracketsAlgo() throws Exception {
17 Algorithm algorithm =new AlgorithmImpl();
18
19 for(int i =0;i<10;i++){
20 System.out.println(algorithm.BracketsAlgo(10,100,1,1));
21 System.out.println(algorithm.BracketsAlgo(1,100,5,1));
22 System.out.println(algorithm.BracketsAlgo(10,100,1,2));
23 System.out.println(algorithm.BracketsAlgo(5,200,4,2));
24 }
25 }
IsRightTest测试类中的部分代码:
1 @Test
2 public void cTest() throws Exception {
3 IsRight isRight = new IsRight();
4 String s[]={ "-n","12", "-m","1","100","-c","-o"};
5 String s1[]={ "-n","12", "-m","1","100"};
6 System.out.print(isRight.cTest(s));
7 System.out.print(isRight.cTest(s1));
8 }
9
10 @Test
11 public void mTest() throws Exception {
12 IsRight isRight = new IsRight();
13 String s[]={ "-n","12", "-m","1","100","-c","-o"};
14 String s1[]={ "-n","12", "-m","1","100"};
15 String s2[]={ "-n","12", "-m","1","10000"};
16 String s3[]={ "-n","12", "-m","333","100"};
17 String s4[]={ "-n","12", "-m","60","100"};
18 String s5[]={ "-n","12", "-m","1","2"};
19 System.out.print(isRight.mTest(s));
20 System.out.print(isRight.mTest(s1));
21 System.out.print(isRight.mTest(s2));
22 System.out.print(isRight.mTest(s3));
23 System.out.print(isRight.mTest(s4));
24 System.out.print(isRight.mTest(s5));
25 }
构造测试数据的思路:尽可能多的把可能出现得情况列举出来,提高代码覆盖率。又因为产生的符号的随机数都是随机的,因此应该在测试时多测试几次,降低偶然性。
单元测试的测试覆盖率截图:
在设计异常时,我将异常都封装到一个名为MyExpection类中,让代码更加简洁。
异常一:输入参数错误。
1 @Test
2 public void mTest() throws Exception {
3 IsRight isRight = new IsRight();
4 String s1[]={ "-n","12", "-m","1","rrrr"};
5 String s4[]={ "-n","12", "-m","ww","100"};
6 System.out.print(isRight.mTest(s1));
7 System.out.print(isRight.mTest(s4));
8 }
如在命令行输入时:
异常二:result.txt文件创建失败
1 @Test
2 public void creatFile() throws Exception {
3 CreatFile creatFile = new CreatFile();
4 creatFile.creatFile(10,1,100,4,1,1);
5 }
如果错误,抛出异常:
其他的异常,主要是利用了java自带的异常。比如说ServletException, IOException, ScriptException等。
如下图所示,关于界面设计,我用的是web页面来进行显示。主要包括index.jsp、welcome.jsp、Algo.jsp、doAlgo.jsp、dologin2.jsp、successUpload.jsp、result.jsp 、login.jsp、register.jsp 、allScore.jsp、studentScore.jsp这十一个jsp文件在浏览器上进行信息的显示。
各个jsp的功能及关系可以参考下图:
1.index.jsp
关于选择语言这个功能,我采用的是将选择的值存在session。只要在该页面选择了 中文/English ,在以后的页面,显示内容前都会加一个判断,判断session中的值对应的选择的语言是中文还是英文。
1
2.login.jsp
该页面用户可以进行登录,登录成功后跳转到welcome.jsp页面,并提供前往登录页面的按钮。
3、register.jsp
该页面可进行用户注册,注册成功后跳转到登录页面,进行用户登录。
4.welcome.jsp
1
5.Algo.jsp
1
27
6.successUpload.jsp
如果文件上传成功则跳转到该页面,提示用户做题时选择题目显示的方式(题目一次全部显示/题目一道做完后显示下一道),并且点击开始做题时,开始计时。
7.doAlgo.jsp
1
2 <%--、、计时--%>
3
8.doLogin2.jsp
9.result.jsp
显示结果部分代码:
1 <%--显示结果--%>
2
3 <%--语言为英语--%>
4
5 Altogether ${count} questions,The number of correct questions is ${right},
6 The number of incorrect answers is ${count-right}
7 ,total time ${allTime2}
8
9 The wrong number is:
10
11 <%--语言为中文--%>
12
13 一共${count} 道题 , 作对 ${right}道 ,
14 做错${count-right} 道 ,共用时 ${allTime1}
15
做错的题号是:
16
17
18
19 ${rightNum}
20
21
22
10.allScore.jsp
该页面看看到全部用户的做题记录及成绩
11.studentScore.jsp
该页面可以查看登录用户以往的做题记录,包括正确率、用时等。
九、界面模块与计算模块的对接:
在该部分,我主要用的是jsp和servlet来将前端与后端进行对接。我主要写了两个servlet方法。AlgorithmServlet、UploadTitleServlet和UserServlet。通过jsp页面将参数传递给servlet方法,servlet获取jsp的相关参数,调用方法将产生的相关内容再返回给jsp页面,在前台展示。
1.确定页面使用的语言
通过在jsp页面中获取用户选择的语言,将相关参数传递给AlgorithmServlet,将语言参数保存在session中。在每个页面中都进行判断session中语言的类型。根据语言的类型,来显示不同的内容。
servlet中存取语言类型 代码:
1 String language = request.getParameter("language");
2 HttpSession session=request.getSession();
3 session.setAttribute("language",language);
jsp中判断部分代码:
1
2
3
4
5
6
2.生成运算式
将运算式需要的参数在页面填写,通过form表单将填入的参数传到servlet中,通过servlet调用产生运算式的方法,将产生的运算式写入txt文件中,用户可进行下载。为了保证同时多人访问网页生成的文本文件冲突,因此,我用一个随机函数,生成一串字符作为写入文件的文件名。如果输入的参数不符合要求,则重新返回jsp页面,提示用户重新输入参数。
随机产生文件名的代码:
String fileName = UUID.randomUUID() + "result.txt";
3.上传文件
首先,通过jsp页面将上传的文件传入servlet中,servlet将该文件保存在程序中,然后通过方法将该文件中的每一行读取出来,存入List中,再将list传到jsp页面,进行题目的展示。
4.判断题目对错
首先将用户回答的原题跟答案传到servlet页面,通过调用方法将该题目的正确答案算出,然后将正确答案跟用户回答的答案进行比较,如果正确,正确的数量加一。如果错误,将错误的题号记录下来。
当用户将题目全部答完,将用户回答的全部题目及答案,还有用户回答对几道题,答错的题号有哪些,用时等信息传给result.jsp页面,通过该页面将信息显示给用户。
5.判断登录用户名是否正确,密码是否正确,不正确时会返回前台给出提示。
后台代码:
1 private void LoginCheck(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
2 String studentNumber = request.getParameter("studentNumber");
3 String password = request.getParameter("password");
4 UserDao userdao = new UserDaoImpl();
5 User user1= userdao.SelectUserBystudentName(studentNumber);
6 HttpSession session=request.getSession();
7 RequestDispatcher rd = null;
8 if (user1!= null) {
9 String username = user1.getUsername();
10 session.setAttribute("username",username);
11 session.setAttribute("studentNumber",studentNumber);
12 if (user1.getPassword().equals(password)) {
13 rd = request.getRequestDispatcher(WebContents.WELCOME);
14 rd.forward(request, response);
15 } else {
16 rd = request.getRequestDispatcher(WebContents.LOGIN);
17 request.setAttribute("msg1", "密码错误,请重新输入!");
18 request.setAttribute("msg2", "The password is wrong. Please retype it!");
19 rd.forward(request, response);
20 }
21 } else {
22 rd = request.getRequestDispatcher(WebContents.LOGIN);
23 request.setAttribute("msg1", "该用户不存在,请重新输入用户信息!");
24 request.setAttribute("msg2", "The user does not exist, please reenter the information!");
25 rd.forward(request, response);
26 }
27 }
6.判断注册用户时,学号是否已存在(学号唯一),不符合要求则返回前台,给出提示。
后台代码
1 private void Register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
2 RequestDispatcher rd = null;
3 UserDao userdao = new UserDaoImpl();
4 if (userdao.SelectUserBystudentName(request.getParameter("studentNumber")) != null) {
5 request.setAttribute("msg1", "用户名已经存在,请重新注册!!!");
6 request.setAttribute("msg2", "The user name already exists, please reregister.!!!");
7 rd = request.getRequestDispatcher(WebContents.REGISTER);
8 rd.forward(request, response);
9 } else {
10 User user = new User();
11 user.setUsername(request.getParameter("username"));
12 user.setStudentNumber(request.getParameter("studentNumber"));
13 user.setPassword(request.getParameter("password"));
14 System.out.println(user);
15 if (user != null)
16 if (!user.getPassword().equals("")) {
17 userdao = new UserDaoImpl();
18 userdao.AddUser(user);
19 request.setAttribute("msg1", "注册成功,请登录!!!");
20 request.setAttribute("msg2", "Registration is successful, please login.!!!");
21 rd = request.getRequestDispatcher(WebContents.LOGIN);
22 rd.forward(request, response);
23 } else {
24 rd = request.getRequestDispatcher(WebContents.REGISTER);
25 request.setAttribute("msg1", "输入信息不完全,请重新输入!!!");
26 request.setAttribute("msg2", "The input information is incomplete,please reenter the information.!!!");
27 rd.forward(request, response);
28 }
29 else {
30 rd = request.getRequestDispatcher(WebContents.REGISTER);
31 request.setAttribute("msg1", "输入信息不完全,请重新输入!!!");
32 request.setAttribute("msg2", "The input information is incomplete,please reenter the information.!!!");
33 rd.forward(request, response);
34 }
35 }
7.展示登录用户做题的历史记录。
后台代码:
1 private void ListUserScore(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
2 List list = new ArrayList();
3 RequestDispatcher rd = null;
4 HttpSession session=request.getSession();
5 UserScoreDao userScoreDao =new UserScoreDaoImpl();
6 String sNo = (String) session.getAttribute("studentNumber");
7 list = userScoreDao.InquiryBystudentNumber(sNo);
8 request.setAttribute("list",list);
9 rd = request.getRequestDispatcher(WebContents.SNO);
10 rd.forward(request,response);
11 }
1.index.jsp(确定页面的语言)
2.login.jsp(用户登录)
若输入错误,将提示:
3.register.jsp(用户注册)
若不符合要求,进行提示:
4.welcome.jsp ( 选择功能)
5.Algo.jsp(生成题目)
6.下载文件
7.successUpload.jsp(选择做题方式)
8.doLogin2.jsp(题目一个一个展示)
9.Algo.jsp(题目全部显示)
10.result.jsp(结果显示)
10.student Score (登录用户的历史记录)
12.allScore.jsp页面
十、结对过程:
因为我们之前都是工作室后端的成员,也一起接触过网页版项目的开发,因此我们决定开发网页版的作业。又因为我们之前都是后端的,对前端的知识不是很熟悉。因此我们决定先实现功能,有时间再美化页面。
我们的结对作业的完成主要分为四个阶段:
第一个阶段,完成出题功能并测试;
第二个阶段,完成做题功能并测试;
第三个结算,将前端页面与后台代码进行对接;
第四个阶段,进行博客的书写。
第五个阶段,进行附加功能的代码编写及博客的完善。
优点:在一起可以共同学习,交流想法,对于我们新手写作业,结对编程可以保证我对这个项目所有功能的实现都能掌握。比起每人负责一个功能的编写能学到更多的知识。
在一起编程还可以提高团队合作能力。
缺点:对于这次作业来讲,效率会比较低。 两个人做一个项目,可以按功能进行分工,最后整合。如果进展顺利的话,可以节省很多时间。但是结对编程,需要两人用一台电脑,会增加项目的开发时间。
柯招坤 :
我(陈芳):
PSP2.1 |
任务内容 |
计划共完成需要的时间(min) |
实际完成需要的时间(min) |
Planning |
计划 |
1*60 |
1.5*60 |
· Estimate |
· 估计这个任务需要多少时间,并规划大致工作步骤 |
1*60 |
1.5*60 |
Development |
开发 |
50*60 |
60*60 |
· Analysis |
· 需求分析 (包括学习新技术) |
5*60 |
8*60 |
· Design Spec |
· 生成设计文档 |
2*60 |
2*60 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
1*60 |
1*60 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
1*60 |
2*60 |
· Design |
· 具体设计 |
3*60 |
4*60 |
· Coding |
· 具体编码 |
26*60 |
29*60 |
· Code Review |
· 代码复审 |
4*60 |
4*60 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
8*60 |
10*60 |
Reporting |
报告 |
7*60 |
10*60 |
· Test Report |
· 测试报告 |
5.5*60 |
7.5*60 |
· Size Measurement |
· 计算工作量 |
30 |
30 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
1*60 |
2*60 |
1.项目的缺点:
这次项目虽然完成了,但是做的并不是很完善,首先附加功能并没有都实现,只实现了两种语言的切换,以及记录用户做题信息的功能,还差用户可以随意添加语言功能。其次可能是因为算法的原因,在生成运算式时,有些特定条件在产生运算式过程中特别慢。因此,在测试时,如果过了挺久没出来,就换个参数吧,或者重新再来一次。
2.第一次体验结对编程的模式,首先介绍一下我的队友,柯招坤,一个可爱的男孩子。
这次作业让我做的很艰难,大概花了十天左右的时间。首先,感觉自己做的特别赶,可能是因为清明出去玩了两天的原因吧,从上周五开始,到现在,只要没课就在做这个项目。这个项目对我而言比较难的是做题部分,各种条件的限制。做题部分上传文件的解析、还有将项目放到服务器上(放到服务器上真的要谢谢马福孝大佬)。
3.这次项目做完,尤其是放到服务器上时,还是挺自豪的。毕竟这个是第一个放到服务器上的项目啦。
----------------------------------------------------------------------------------------------------------------------