结对编程项目总结
一、项目需求分析与功能总结
(1)用户注册功能
用户提供手机号码,点击注册将收到一个注册码,用户可使用该注册码完成注册。
(2)设置密码功能
密码6-10位,必须含大小写字母和数字。
有两种场景需要修改密码:
①新注册用户首次设置密码,用户输入两次密码匹配后设置密码成功。
②已注册用户登录状态下修改旧密码。用户在登录状态下可修改密码,输入正确的原密码,再输入两次相同的新密码后修改密码成功。
(3)设置试卷功能
①选择题目难度类型:小学、初中或高中。
②设置题目数量。
(4)生成试卷功能
试卷以2种方式进行展示:
①界面选择题形式。全部题目为选择题,界面显示第一题的题干和四个选项,用户选择四个选项中的一个后提交,界面显示第二题...直至最后一题。
②完整试卷文件保存。生成的题目将以“年-月-日-时-分-秒.txt”的形式保存,每个账号一个文件夹。每道题目有题号,每题之间空一行。不同的试卷难度题目要求不同。
(5)试卷批改功能
①计算随机题目的正确答案,随机作为某个选项。
②最后一题提交后,界面显示分数,分数根据答对的百分比计算。
(6)各功能间的逻辑衔接与补充
①用户在分数界面可选择退出或继续做题。
②输入类的信息都需要符合实际的验证。例如:用户填入的是不是合理的手机号码,设置的密码是否符合规范。
二、如何复用个人项目实现结对编程项目
队友和我在个人项目阶段都很有默契的选择了C++实现。得知结对编程项目的需求后,我们两个人考虑到写UI界面的复杂度以及短信模块的添加实现,没怎么纠结的就一致决定用JAVA开启我们的新征程啦!
对个人项目的复用情况,我们套用原先代码再加以适度的语言转化修改,以及拓展功能的实现。JAVA和C++虽然有不同的语言书写习惯,但还是有很多相通和能够代码重用的部分。鉴于个人项目中,我的版本能够实现题目满足各难度要求的符号以及括号的随机生成,而队友的版本初高中试题省略了括号的存在不够全面,因此结对项目中与个人项目重叠的功能逻辑就基于我的个人项目代码修改完成了。
在此之上,我们一同针对项目需求拓展了根据随机生成的题目计算答案、题目界面、登录界面、设置密码界面以及短信验证功能。
三、项目类说明与类中方法解读
1、Calculation
(1)功能:计算题目答案
(2)计算题目答案的解决思路
首先将中缀表达式转化后缀表达式(逆波兰表达式),然后利用栈计算后缀表达式。
①中缀表达式转后缀表达式
如果遇到操作数,我们就直接将其保存在表示后缀表达式的栈中。如果遇到操作符,我们将其放入到专门存储操作符的栈中,遇到左括号时我们也将其放入存储操作符的栈中。如果遇到一个右括号,则将存储操作符的栈元素弹出,将弹出的操作符输出到表示后缀表达式的直到遇到左括号为止。注意,左括号只弹出并不输出到表示后缀表达式的栈。如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到" ) "的情况下我们才弹出" ( ",其他情况我们都不会弹出" ( "。如果我们读到了输入的末尾,则将栈中所有元素依次弹出到表示后缀表达式的栈中。
②计算后缀表达式
标准的表达式,在数学上称为中缀表达式,其运算符在两个操作数的中间,例如:a*b+(c-d/e)*f。中缀表达式是人们常用的算数表示方法,但它不容易被计算机解析,因为要先判断有无括号,再根据运算符的优先级运算,这是很麻烦的。后缀表达式的运算符则紧跟在两个操作数之后,例:ab*cde/-f*+。后缀表达式的求值规则为:从左到右扫描后缀表达式,如果遇到操作数,将其压入栈中,如果遇到“+”,“*”等操作符,则从栈中弹出两个操作数;或遇到“sin”,“cos”等操作符对一个操作数处理即可。计算结果,然后把结果入栈;遇到直到遍历完后缀表达式,则计算完成,此时的栈顶元素即为计算结果,代码中这个栈是store。
(3)各个方法的说明与解读
方法① public int JudgeSign(String ch)
作用:定义符号优先级。
内容:将平方、开方、三角函数设为最高的运算等级,乘法和除法次之,加法和减法最低。
方法② public double BeginCalu(double x,double y,String p)
作用:处理需要两个操作数的运算符计算。+ - * \
方法③ public double BeginCalues(double x,String p)
作用:处理仅需要一个操作数的运算符计算。√ ^2 三角函数
内容:调用math方法。实现方法例如:Math.cos(Math.toRadians(x)),Math.sqrt(x),Math.sin(Math.toRadians(x))等。
方法④ public String Calu(String array)
数据结构说明:
Stack<String> num = new Stack<String>();//存操作数的栈
Stack<String> op = new Stack<String>();//存操作符的栈
Stack<String> df=new Stack<String>();//存储num栈变成正常顺序
Stack<String> store=new Stack<String>();//存储读到的数字
String array //从文件中读取到的随机生成题目
String[] getmm//将一道完整的题目去除等号,将各个操作数、操作符独立拆分
内容:
首先将从文件中读取出来的一道题目在操作数和操作符间添加空格,然后以空格辅助拆分各个操作数和操作符。然后根据我已在解题思路中说明的规则将中缀表达式转化成后缀表达式。最后根据规则计算后缀表达式的结果。Store的栈顶元素是最终的结果,num,op,df都可理解成中间的辅助栈。
2、ChangeTitle
(1)功能:答题界面显示-切换题目,统计答题得分,答题界面显示-选项随机
(2)各个功能的解决思路
①答题界面显示-切换题目
这部分主要是理解java的GUI程序。涉及到两个重要的点。
Java的GUI程序的基本思路是以JFrame为基础,它是屏幕上window的对象,能够最大化、最小化、关闭,当然这些是否被允许也是可以自己设置的。我们在设计的时候考虑到美观性就预先设置不要用户最大化了。Swing 的三个基本构造块:标签、按钮和文本字段;但是需要个地方安放它们,并希望用户知道如何处理它们。JFrame它是一个容器,允许程序员把其他组件添加到它里面,把它们组织起来,并把它们呈现给用户。
JPanel是 Java图形用户界面(GUI)工具包swing中的面板容器类,包含在javax.swing 包中,是一种轻量级容器,可以加入到JFrame窗体中。JPanel默认的布局管理器是FlowLayout,其自身可以嵌套组合,在不同子容器中可包含其他组件,功能是对窗体上的这些控件进行组合。
在多个题目切换的过程中,因为不同的题目还是有一定关联性的,所以我们是采用类似数组的结构,在逻辑上和物理操作上对一张试卷的所有题目进行关联。
只有一个窗口:JFrame frame = new JFrame();
对面板容器、组件按钮采用数组结构进行关联,用ButtonGroup实现单选:
JPanel[] p = new JPanel[num];//题目
int[] randomchoose = new int[num];
JLabel[] la = new JLabel[num];
JRadioButton[] a = new JRadioButton[num];
JRadioButton[] b = new JRadioButton[num];
JRadioButton[] c = new JRadioButton[num];
JRadioButton[] d = new JRadioButton[num];
ButtonGroup []xuan = new ButtonGroup[num];
其实在写着一部分的时候我们也是反复修改的:关键点①是要考虑到实际应用界面的用户需求,比如“上一题”这个按钮在第一道题是不需要的,“下一题”在最后一页是不需要的,相应要修改成其他的按钮。关键点②是不要忘记了JPanel[] p切换的逻辑,这一点我们在实现题目切换的时候出现了bug,发现重回上一道题所有的题目会逐渐叠加在一起。我和队友重读代码才意识很重要的一点:当前要显示某一页的时候,要确保只有这个面板是可视的,保险起见还是把所有其他的面板都setVisible(false)。
②答题界面显示-选项随机
这一部分我写的时候是利用了数组和随机数,并且对其余的三个选项也进行了随机。
③统计答题得分
其实这些功能的实现都是有穿插的。虽然最后的分数我是在全部题做完以后才计算的,但是在用户选择每道题目的过程中,我就已经对用户的选择做了标记,这样只要最后统计我用来进行标记的数组就好了。然后对分数进行了四位小数保留。
至于具体的行为的改变调用相关监听方法即可。
(3)方法的说明与解读
在这个类中只写了这一个方法 public void init(int num ,String path,String name),实现了以上我详述的全部的功能。
3、FindLike
(1)功能:题目查重
(2)解决思路
其实这个部分的思路是跟我在C++个人项目中写的是一样的,相当于进行的是代码翻译。从某种角度来讲比较这一模块的C++和JAVA代码其实就可以感觉的出JAVA语言的小优点吧,JAVA对用户处理文件是比较友好的。当时C++文件流的部分我写到头秃(当时文件流这边出的bug是让我熬夜到凌晨三点的罪魁祸首),但是JAVA相当短的几行代码就能完成相同的任务量,简直感动T^T。
(3)方法的说明与解读
在这个类中也只写了这一个方法public void find(int n,String name,String le,String thispath)就能实现查重功能了。
4、MainUi
(1)功能:主界面,实现登录和调用注册验证功能
(2)解决思路
这个模块从用户的角度就是使用的起始,是软件的门面。在设计这一部分的时候需要注意的时候从用户角度使用时的业务逻辑,页面的各个按键在这个部分会比较多,重点是要细心的注意调用方法.setVisible,这样可以避免一些意想不到的错误。如果用户首次使用软件,就要调用输入手机号码并且发送验证码验证的功能。其中页面的逻辑其实是不断的信息匹配验证和合理性验证性。比如我们在用户输入信息的时候首先就验证了是否是11位的全部是数字的手机号码,然后如果手机号码合理就new一个后面我会详细说明的发送验证码的类,然后用过验证码传参和通过界面组件取值来进行信息的比对判断是否验证成功。密码的部分也是万变不离其宗,多次界面组件取值然后进行输入与原值的比较进行验证,然后更新用户的信息。
代码实现上第一点是要学会JAVA关于事件处理的一个接口ActionListener,因为界面设计的时候用户会根据按钮上的字来选择他们想要进行的功能,那么代码怎么意识到用户的选择动作呢?ActionListener用于接收操作事件的侦听器接口。对处理操作事件感兴趣的类可以实现此接口,而使用该类创建的对象可使用组件的addActionListener 方法向该组件注册。在发生操作事件时,调用该对象的actionPerformed 方法。第二点就是用到了监听的相关方法。比如,我们反复用到了(boolean)按钮.getActionCommand().equals("按键提示信息")来判断用户选择了什么功能。总而言之是要熟练掌握组件的相关方法。
(3)方法的说明与解读
在这个类中也只写了这一个方法public void ui()实现了界面的组件声明和组合,以及义务逻辑上单纯登录和注册等功能的关联性。public void ui()中调用了ActionListener接口,然后在其中创建了public void actionPerformed(ActionEvent e)的方法实现逻辑上的验证和跳转。
5、MakeTitle
(1)功能:设置试卷相关信息
(2)解决思路
这个部分是涉及通过界面跟用户进行交互,然后确定生成试卷的难度类型还有题目的数量,然后生成符合要求的题目。
其实通过详述part 4之后,其实这一类型的页面和逻辑跳转都是万变不离其宗的,甚至MakeTitle这一part比MainUi要简单实现很多。仍旧是牵涉到组件的监听、调用相关方法判断用户操作、对所选的信息进行标记、通过读取文本框的输入信息进行题目数目的合理性比对,还有是要生成试卷部分有牵扯到文件的新建和写入以及题目的随机生成、题目查重、页面的切换题目。后面的这几个就是现在这部分有了逻辑上的关联,具体的又新建了类来逐个解决。
(3)方法的说明与解读
这个类中是实现了一个方法public void init0(String name),声明组织了界面的组件分布和相关的设置,然后ActionListener()监听用户的操作,然后信息匹配核查后调用新的其他功能的类方法,共同组合。
很多用到的注意点在part 4MainUi中已有了详细的论述说明,此处不再赘述。
6、OpenFile
(1)功能:打开文件得到文件内容
(2)解决思路
这一部分就是能很好在C++和JAVA语言实现时比较出JAVA的简便性。在文件的相关方法JAVA里还是存在并且方便用户直接使用的,不像C++中要自己写很多。
(3)方法的说明与解读
public String[] readTxtFile(String filePath)方法就是先判断某个文件在不在,要是不在就用文件的相关方法直接创建也很方便,要是有的话那就是文件中有之前随机生成的题目,但注意是有题号的,所以读完都存在了一个字符串数组里,还要对原先的信息去题号,只单纯保留题干信息。
7、PhoneCode
(1)功能:发送短信验证码
(2)解决思路
完成这个部分我注册了阿里云服务平台,申请了它的短信服务平台,注册了自己的签名和模板。这一部分我觉得重点在于如何配置信息,去初始化与自己账户相关的函数,然后跟阿里云服务平台进行一个交互信息传递,去实现一个完整的接口调用即可。AccessKey ID和AccessKey Secret是访问阿里云API的密钥,具有该账户完全的权限,这就是写代码识别我注册时候的用户身份的唯一方式。DTS sdk 主要包括两个核心包,aliyun-java-sdk-core-inner 和 aliyun-java-sdk-dts, 其中aliyun-java-sdk-core-inner的主要作用为DTS endpoint的自动路由; aliyun-java-sdk-dts 这个包包含了dts所有功能的相关类。需要设置Access Key ID 和 Access Key Secret、超时时间、初始化ascClient参数和默认的地域信息。需要组装请求对象,然后使用post提交。通过.getAcsResponse、.getCode相关方法信息交互。6位的验证码我通过随机数生成然后取模得到。
(3)方法的说明与解读
方法public String getPhonemsg(String mobile)在逻辑上串联所有上述的功能。方法public static String vcode()随机生成六位的短信验证码。
8、SetFile
(1)功能:新生成试卷文件路径
(2)解决思路
很简短的几行代码生成当前时间的命名文件,然后以string形式返回路径。
(3)方法的说明与解读
在这个类中只写了这一个方法public String setname(String name,String le)就能实现功能了。
9、StaticPeram
(1)功能:短信校验的参数设置
(2)解决思路
这部分我单独提出是为了使我之前讲到的短信验证模块通用性更强,于是就想到把超时时间、ascClient参数、accessKeyId、accessKeySecret、签名和模板信息全部单独设置,方便管理。
10、SetPass
(1)功能:设置密码
(2)解决思路
牵扯到的知识点分为界面显示部分和密码的验证。
Java的GUI程序的基本思路是以JFrame为基础。Swing 的三个基本构造块:标签、按钮和文本字段。JFrame它是一个容器,允许程序员把其他组件添加到它里面,把它们组织起来,并把它们呈现给用户。JPanel是 Java图形用户界面(GUI)工具包swing中的面板容器类,包含在javax.swing 包中,是一种轻量级容器,可以加入到JFrame窗体中。不同子容器中可包含其他组件,功能是对窗体上的这些控件进行组合。
密码的验证就是符合描述逻辑即可,读文本框内容,读文件内容进行比对;或者读文本框内容进行对文件的写操作。
(3)方法的说明与解读
①方法public int yanzheng(String s)
密码6-10位,必须含大小写字母和数字,通过.isDigit、.isUpperCase、.isLowerCase等方法判断,验证密码的正确性。
②方法public void Writeuser() throws IOException
将用户信息写入到user.txt一个用来记录所有注册用户信息的汇总文件中。如果是新注册账户就需要包括用户名和密码全部录入,如果已经注册过那就通过搜索user.txt文件验证即可。
③方法public int GetName(String na) 得到账户名。
④方法public String Getpass(String na) 得到密码。
⑤方法public void Changepassword(String name,int flag)
实现密码修改的情况。有两种场景需要修改密码:①新注册用户首次设置密码,用户输入两次密码匹配后设置密码成功。②已注册用户登录状态下修改旧密码。用户在登录状态下可修改密码,输入正确的原密码,再输入两次相同的新密码后修改密码成功。通过窗口、面板组件上的信息读取对比,必要的时候要通过文件流对信息进行读取和修改。
11、SetTitle
(1)功能:生成不同难度的试题
(2)解决思路
跟我的个人项目中思路一致。
首先要确保不同的难度要求的符号都全部出现,然后括号m个操作数最多会有(m-2)组括号,那么通过随机数随机判断每组括号的情况,还需要对随机产生的结果进行校验,删除各个组合后无意义的情况,以及在整整表达式中没有必要的部分。然后操作数是通过随机数取模在范围内。操作符号也是随机生成,当然遇到sin,cos等特殊的符号还有在前或在后添加操作符的过程。然后将操作数、操作符、括号三者组合成一个表达式即可。
(3)方法的说明与解读
①public String RandOperator(int randnum)数字映射操作符。
②public int JudgeBracket(int NumInProblem,int bracket[])判断括号随机情况的合理性。
③public void Generate_Level(int NumInProblem,String PaperType,FileWriter writer)生成某种类型的试题
④public void GenerateDifferntPaper(int NumOfProblem,String PaperType,String FileName) throws IOException生成不同类型的试卷
四、结对编程的经验与教训
这次结对编程我跟队友小姐姐的合作还是十分流畅和愉快哒~虽然两个人遇到了不少bug,但是我们都很频繁的进行交流和沟通,有不懂的就彼此之间一起学习,集思广益,效率很高~╰(●’◡’●)╮撒花撒花~
还有很重要的一点就是身为程序女孩永远不能认输(ง •̀_•́)ง大概做中学真的是很能提高学习效率,感觉短短的时间里我和队友小姐姐两个人学到了也熟悉了很多组件啊以及JAVA对文件处理的比较友好的方法。
经验之一需要修炼的就是有时改bug时难以抑制的急躁的心情吧,心态要尽量平稳呀!遇到过一些莫名其妙的bug。还记得当时自己感觉特别简单的一个逻辑点就是结果出来不对,自己排除了好多种可能出错的情况,中间有些头秃……不过在找度娘、询问小伙伴的过程中也都被解决啦!所以遇到问题别慌,善于百度找经验贴或者问问同学都是会解决的。
教训其二是觉得以后做项目要结合老师上课讲的,提前做好规划,稍微大型的项目一定要有原型,这样事半功倍。比如这回实现中有很多界面。一开始是队友写了几个界面的初始版,等我开始美化的时候我意识到,由于最开始我们没有设计好界面的整体效果,很有可能就是动一发而牵全身,就比如如果把按钮什么放的紧密一点,这样如果修改了某一个东西的字体大小,那就有可能出现组件看上去重叠,后期一点一点优化界面就有些头秃。所以要提前设计规划,自顶向下的业务逻辑更好。
这个过程中我学习到了很多东西,比如JAVA GUI程序呀包括窗口、面板、被包含的很多组件、如何去美化界面,复习了中缀表达式转后缀表达式以及后缀表达式的计算,然后在处理文件相关的时候多次应用了相关处理,熟练了很多调用方法,还有自己整短信模块时候克服了恐难心理吧因为是第一次接触但静下心想想原理还是觉得比较容易理解的,还有跟队友一起改界面切换的bug逻辑关联也是训练了我的思维缜密性。
还有一个是之前做作业我没有考虑和意识过的,就是我和队友小姐姐在反复传修改版本的时候发现开始她的正常运行版本在我的电脑上就是报错,很奇怪。后来俩人研究了一番发现存在必要时要学会配置相关的项目在本机。步骤不是很繁琐,但也挺必要的。这部分我有特意全程每一个步骤都截图文档说明在项目工程文件中说明了操作步骤,方便以后回忆吧~~
总而言之,这次复习、实践、设计、学习到了很多,然后跟队友小姐姐合作也很愉快效率也很高,圆满完成结对编程项目还是很开心哒~继续加油哟!
五、功能及效果展示(每行从左至右阅读)
此时在当前文件夹下会出现账户文件夹。文件夹下会有符合命名要求的试卷题目。