一、前言
本文是对于结对编程队友的个人项目的分析,由于工程量较大,完成分析花了一定的时间。不过有一说一,队友的这项工程完成度是相当高的,质量也是很靠谱。本人在分析队友的工程的同时也是在学习的过程,队友的程序语言采用的是C++,区别于java和python等其他很多同学采用的语言,在队友的这项工程中是可以感受到C++语言的魅力,在相当的程度上是发挥了C++语言的特性,尤其是在框架的架构上,对于分析的本人来说是收获颇多的一次学习。
二、项目测试
1、登录界面
登录界面
输入"-1"可以退出程序,这里已经输入了用户名和密码
登录成功
显示登录成功提示,按任意键后进入菜单界面
登录失败
显示登录失败信息,按任意键后重新登录
2、菜单界面
显示操作帮助信息
题目生成
这里输入15后,按下回车键后生成题目,可以看到相应数量的题目已经生成,且题目难度也符合要求,该图所展示的是小学难度,而初中和高中题目生成如下
说明题目生成的功能是成功实现的
且对应路径下实现了文档的生成与存储。
切换用户类型
输入切换用户类型的指令,按下回车后可以看到切换成功的提示信息
按下任意键,可以看到菜单界面中用户信息显示这一块已发生变化
生成的题目难度也相应发生了变化,说明用户类型是能够成功切换的。
退出登录和切换用户
输入"-1",退出登录
按下回车后返回到登录界面
切换另一个用户是能够重新登录的,说明该功能完成。
无效指令
可以看到对于不符合题目数量要求和错误文本作了两种不同的处理,实现了无效指令的处理。
测试评价
本次项目尚未有交互界面设计的要求,但单从该程序的测试体验上来讲是非常不错的,个人项目当中需求的基本功能是全部实现,且每一步操作都有相应的信息反馈,登录后相应的用户提示和操作帮助的信息,对错误指令的处理也相当完整,不会引起程序无反馈或崩溃,对于没有接触过个人项目说明文档的人也能轻松操作。但从程序的测试方面是没有感受到有什么缺陷,反而是可以体会到该程序对于各方面细节的注意,作为一个项目而言十分完整。
三、代码分析
由于代码量过大,这里不完全展示,仅解释关键部分和文本当中分析到的部分。
本程序主要为两大类:用户类和主程序类
1、用户类
// 用户基类(抽象类)
class UserBase {
public:
UserBase(const std::string& user_name, const std::string& user_password,
const std::string& user_type)
: user_name(user_name),
user_password(user_password),
user_type(user_type) {}
virtual ~UserBase() {}
// 用户生成对应的题目
virtual std::string GenerateProblems() = 0;
// 获取/设置用户名,密码,用户类型
virtual std::string GetUsername() = 0;
virtual std::string GetUserPassword() = 0;
virtual std::string GetUsertype() = 0;
virtual void SetUsername(std::string user_name) = 0;
virtual void SetUserPassword(std::string user_password) = 0;
virtual void SetUsertype(std::string user_type) = 0;
protected:
std::string user_name;
std::string user_password;
std::string user_type;
};
本项目对于用户类提供了抽象接口,其中包含基本属性用户姓名,密码和类型,方法中除了这三个属性的读写函数外,还有GenerateProblems()这一函数用于生成题目,以下在介绍具体实现类时,主要分析这一函数的实现。而继承了基类的又分为三个类
小学用户类:class PrimarySchoolUser : public UserBase
初中用户类:class MiddleSchoolUser : public UserBase
高中用户类:class HighSchoolUser : public UserBase
三个具体实现的类当中Get和Set这类与类属性相关的函数相似,主要区分在生成题目GenerateProblems()这一纯虚函数的实现上有所不同,以小学用户类中的实现为主要例子
std::string GenerateProblems() override {
std::string problem;
while (true) {
// 操作数,即算式中数字的个数,取值范围为2-5
int operation = rand() % 4 + 2;
// 左括号出现的位置,取值范围为0-3
int bracket_left = -1;
// 右括号出现的位置,取值范围为左括号位置+1-操作数-1
int bracket_right = -1;
// 括号已输入完全
bool bracket_flag = false;
if (operation > 2) {
bracket_left = rand() % (operation - 1);
bracket_right =
rand() % (operation - bracket_left - 1) + bracket_left + 1;
}
for (int j = 0; j < operation - 1; j++) {
if (j == bracket_left) {
problem += "(";
}
problem += std::to_string(GenerateRandomNumber());
if (j == bracket_right) {
problem += ")";
bracket_flag = true;
}
problem += GenerateRandomOperator();
}
problem += std::to_string(GenerateRandomNumber());
if (bracket_flag == false && operation > 2) {
problem += ")";
bracket_flag = true;
}
if (bracket_right - bracket_left + 1 != operation) {
break;
}
problem.clear();
}
problem += " = ";
return problem;
}
简单分析方法,先随机生成操作数的个数,再随机定位左括号和右括号加在第几个操作数上,将这些确定之后再进行添加操作数和操作符的循环,最后补上最后一个操作数以及是否缺失的右括号来完成一道题目的生成。
初中和高中的题目生成方法当中,将根号和平方以及三角函数的处理与小学题目中括号的处理方法相似。
2、主程序类
主程序类中的方法过多,因此这里主要介绍该项目框架的架构方面。
主要结构与基本属性
首先私以为该程序的主要结构在于这两张哈希表
std::unordered_map<std::string, UserBase*> users;
std::unordered_map<std::string, std::vector<std::string>>
all_user_problem_list;
该项目在结构方面,主要是让用户与其生成的试卷对应上,以便生成和写入用户所对应路径下的文档,在这两张哈希表的建立下实现了关系的联结。在两个map中,关键字string相同,在users中UserBase*指向具体的用户,在all_user_problem_list中,vector用于存放生成的题目,程序中在Init()函数中将九名用户初始化后,将用户与九名用户题目存放的vector,分别加入到对应的map中,实现了基础框架的实现。
主程序类中的属性除了两个unordered_map和九个用于存放题目的vector,还有一个用户指针用于指示当前用户UserBase* current_user。
方法
该项目的大部分功能的实现都在主程序类中。包括登录,菜单,查重,文件存储等该项目需要的大部分功能,从测试环节中可以得知方法的实现都是没有问题的,因此接下来仅代表我个人看法表达利弊分析。
三、整体总结
这一部分分为优势、缺点、总结三部分,来表达作为分析代码一方的看法。
1、优势
1.从代码书写这一方面来看,该项目代码的书写严格按照谷歌规范,这一方面比较严谨。
2.在用户类中对于随机生成题目的方法上,所采用的想法是比较好理解且实用的,并且生成题目也比较人性化,例如在高中难度的题目当中对于角度的生成也限定了范围,可以感受到这一功能在实现过程中是有站在使用者的角度所考虑的。
3.功能实现上非常完整,并且存在很多细节方面的考虑,例如对无效指令的处理上,在每一条输入后都能得到反馈上,可以感受到项目编写时的严谨与思考的全面性,交互性非常充分。
4.代码风格上,在前言中提到过,本项目的代码是能够让人体会到C++语言的特点,项目中在很多方法上采用了指针与STL结合的方式,可以感受到项目编写者对C++这门语言掌握是比较熟悉的,且这样的方法也比较巧妙。
5.在文件的处理上,经指教后明白了该项目的文件处理采用了C++17当中提供的方法,不仅在文件路径上解决了本人没能解决的文件路径存在中文的问题,并且该文件的迭代器能够自行分别文件与文件夹,在选择文件路径和打开文件上更为灵活,方法上也更为简单,更关键的是,采用该迭代器能够实现同一文件夹下多个文件的读入。
6.最后是整体架构上,两个unordered_map用相同的关键字来进行联系与管理来完成整个项目地架构这一方面感觉非常巧妙。
2、缺点
1.在查重这一方法中,感觉还是存在过于麻烦的问题,查重方法如下:
bool CheckProblem(const std::string& problem) {
PrimaryFileInput();
MiddleFileInput();
HighFileInput();
bool check_flag = true;
for (const auto& i :
all_user_problem_list.find(current_user->GetUsername())->second) {
if (i == problem) {
check_flag = false;
break;
}
}
return check_flag;
}
其中PrimaryFileInput(),MiddleFileInput(),HighFileInput()三个函数的功能类似,在这三个函数中,程序将存放题目的所有vector清空,将对应路径下历史生成的题目全部加入vector中,再在与用户对应的vector中与新生成的题目比较进行查重,每一次查重的工作量比较大,也许可以采用只重新读入对应用户的历史生成题目来减少工作量。
2.私以为主程序类中的方法过多,稍有些复杂,或许与题目相关的操作可以再新建一个类,再该类中加入有关题目操作的方法,或许在整体结构上会更加清楚。
3、总结
整体来说这是一份优秀的案例,无论是在功能的实现上,还是代码书写的规范上,在这两个方面都做的比较完整和严谨,优势大于缺点的程序。另外,作为一项供人使用的程序,该项目很多细节上的考量与思考上的严谨是值得学习的。
标签:std,题目,string,个人,代码,用户,user,problem,UNU From: https://www.cnblogs.com/marinrin/p/17717244.html