本博客为湖南大学2021级软件工程导论课程的结对小组个人项目互评。根据本课程“做中学”的要求,我们第一周完成了个人项目代码,现在结对完成代码互评。
编程语言:C++
结对成员:顾问、郑雨凡
文章作者(评价人):顾问
代码作者:郑雨凡
一、个人项目要求:
用户:
小学、初中和高中数学老师。
功能:
1、命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中各三个账号),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学、初中和高中三个选项中的一个。否则提示“请输入正确的用户名、密码”,重新输入用户名、密码;
2、登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学、初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题。每道题目的操作数在1-5个之间,操作数取值范围为1-100;
3、题目数量的有效输入范围是“10-30”(含10,30,或-1退出登录),程序根据输入的题目数量生成符合小学、初中和高中难度的题目的卷子。同一个老师的卷子中的题目不能与以前的已生成的卷子中的题目重复;
4、在登录状态下,如果用户需要切换类型选项,命令行输入“切换为XX”,XX为小学、初中和高中三个选项中的一个,输入项不符合要求时,程序控制台提示“请输入小学、初中和高中三个选项中的一个”;输入正确后,显示“”系统提示“准备生成XX数学题目,请输入生成题目数量”,用户输入所需出的卷子的题目数量,系统新设置的类型进行出题;
5、生成的题目将以“年-月-日-时-分-秒.txt”的形式保存,每个账号一个文件夹。每道题目有题号,每题之间空一行;
二、代码分析:
代码作者使用C++面向对象编程。现将逐一分析核心代码。
int main() { using namespace user_namespace; Teacher *teacher=new Teacher("1","2","3"); srand((unsigned int)time(0)); while(1) { printf("%s","用户登录\n"); char id[20]; char password[20]; printf("%s","请输入用户名和密码,中间用空格隔开:"); scanf("%s %s",id,password); int result=teacher->CheckUserLogin(id,password); if (result==1) { printf("%s","登陆成功!\n"); teacher->Menu(teacher); } else { printf("%s","请输入正确的用户名、密码\n"); } if (exit_flag==1) { printf("%s","用户已退出,返回初始界面..."); exit_flag=0; continue; } } return 0; }
main函数 先通过using引用命名空间,创建一个Teacher类对象并初始化。接着通过while循环设置登录界面,通过类成员函数checkUserLogin检查输入是否正确。整个函数清晰明了,可读性高。使用名字空间,防止程序中的同名冲突。
//抽象类User class User { public: virtual int CheckUserLogin(char id[],char password[])=0; }; //具体类Teacher继承User,实现主要功能,包括登录、出题、题目查重等 class Teacher:public User { public: Teacher(std::string i,std::string p,std::string t) //构造函数 { strcpy(id_,i.c_str()); strcpy(password_,p.c_str()); strcpy(type_,t.c_str()); } int CheckUserLogin(char id[],char password[]);//检查输入用户名密码是否正确 void Menu(Teacher* teacher);//登录成功后菜单界面 void ChangeType(Teacher* teacher);//切换试卷类型 void GetTime(std::string &filename);//获取系统时间 void CreateTestTXT(Teacher* teacher,std::string &filename);//在对应用户的目录下创建题目文件 void WriteTestTXT(const std::string &subject,const std::string &filename,int &subject_number);//将题目写进对应名称的文件 int CheckTwo(Teacher* teacher,const char &symbol_two,std::string &s1,std::string &s2);//检查生成包含两个操作数的算式是否重复 int CheckNotTwo(Teacher* teacher,const std::string &subject);//检查生成的不是两个操作数的算式是否重复 void WriteBankTXT(Teacher* teacher,const std::string &subject);//将所生成的题目写入题库,方便查重 void CreateNumber(std::vector<std::string> &number,std::vector<char> &symbol);//随机生成操作数和加减乘除符号 void CreateMiddleSymbol(std::vector<std::string> &number);//随机生成平方和根号符号 void CreateHighSymbol(std::vector<std::string> &number);//随机生成三角函数符号 void CreateBrackets(std::vector<std::string> &number);//随机生成括号 int TestBrackets(std::vector<int> &brackets_pos,int &pos);//检查是否有不必要的括号存在 void CreateSubject(int &question_num,Teacher *teacher);//生成题目 private: char id_[20];//账号 char password_[20];//密码 char type_[5];//小初高类型 //所含属性信息 };
抽象类接口user,可拓展性高,方便后续添加其他用户类。派生类Teacher继承user类,实现了具体登录出题等功能,功能清晰,易于理解使用。
三、运行测试:
开始界面:
登录成功:
切换类型:
重新登陆:
生成题目:
四、总结:
优点:
- 面向对象编程,使用抽象类和继承,灵活、重复可用、可拓展性强。使用user类和派生类Teacher,随着用户增加可以拓展用户类。代码容易维护和开发,方便接下来结对项目使用。
- 代码安全性、封闭性好。Teacher类中对关键信息和函数采用private保护,防止信息泄露。使用名字空间,防止函数变量重名,有利于后续开发。
- 关于查重功能,将生成的题建立题库,方便后续查重。查重不仅仅是读入字符串相互比较,对于两个操作数的题目有额外查重。
- 生成题目时,先随机生成的操作数和加减乘除符号放入vector容器中,再看题目类型,初高中题目就向容器中加入相应符号,实现起来很清晰简便。
- 代码设计合理,所有方法函数相辅相成,而不是写在一起,便于理解、使用和维护。函数不超过40行,便于阅读。符合谷歌代码规范,代码格式正确、注释清晰、命名符合规定,便于阅读和修改。
- 代码功能完备,能实现不同类型出题和切换。命令行文字提示清晰明了,便于用户使用。
缺点:
- 只有两个类,user抽象类和Teacher派生类。把许多方法函数写在Teacher类中,包括出题和存放文件的方法函数。其实可以多写几个类,让方法函数可重复利用。
- 方法函数之间相互调用,注释没有十分清晰注明方法函数功能,阅读有一点困难。