结对编程之个人项目代码互评
简介
这篇文章是结对编程的个人项目代码互评。
我的结对编程搭档是杨廷元同学(以下简称杨少,笑),下面就对杨少的代码进行简单的总结评价。
项目需求
- 假想用户为小学、初中和高中数学老师;
- 要完成的功能有:
- 命令行输入用户名和密码,两者之间用空格隔开;
- 用户登录后根据账户类型提示要生成的题目类型,此时用户可以输入需要的题目数量,该数量在 10 到 30 之间,包括 10 和 30,如果用户输入 -1 则退出当前账户重新登录;
- 要将生成的题目保存到文件,以"年-月-日-时-分-秒.txt"的形式保存,每个账号一个文件夹,每道题目有题号,每题之间空一行,新生成的题目不能和已存在的重复;
- 用户可以在登录后切换类型选项,符合要求的类型选项只有小学、初中和高中。
- 补充说明:
-
预置账户:
账户类型 账户 密码 小学 张三1 123 小学 张三2 123 小学 张三3 123 初中 李四1 123 初中 李四2 123 初中 李四3 123 高中 王五1 123 高中 王五2 123 高中 王五3 123 -
题目难度要求:
小学 初中 高中 难度要求 +,-,*./ 平方,开根号 sin,cos,tan 备注 只能有+,-,*./和() 题目中至少有一个平方或开根号的运算符 题目中至少有一个sin,cos或tan的运算符
-
代码结构
杨少的代码分了三个类,来一一分析一下。
用户类 User
/**
* Users 用户类
*/
public class Users {
private Map<String, String> controller = new LinkedHashMap<>();
private Map<String, String> userPrimary = new LinkedHashMap<>();
private Map<String, String> userMiddle = new LinkedHashMap<>();
private Map<String, String> userHigh = new LinkedHashMap<>();
public Map<String, String> getController() {
return controller;
}
public Map<String, String> getPrimary() {
return userPrimary;
}
public Map<String, String> getMiddle() {
return userMiddle;
}
public Map<String, String> getHigh() {
return userHigh;
}
/**
* userInit 用户类初始化
*/
public void userInit() {
controller.put("Mr.Yang", "091520");
userPrimary.put("张三1", "123");
userPrimary.put("张三2", "123");
userPrimary.put("张三3", "123");
userMiddle.put("李四1", "123");
userMiddle.put("李四2", "123");
userMiddle.put("李四3", "123");
userHigh.put("王五1", "123");
userHigh.put("王五2", "123");
userHigh.put("王五3", "123");
}
}
可以看到这里的成员属性包括四个 Map,分别存储管理员账户以及三种类型的教师账户。类的方法包括四个属性的 get 方法和一个可以将账户放入 Map 的初始化方法。
出题类 ProblemSetting
由于代码量较大,就放出类的属性和方法声明,不贴所有方法的具体实现了。
/* 读写对象 */
public BufferedWriter bufferedWriter;
/* 左括号 */
static List<String> bracketsLeft = Arrays.asList("(", "", "(", "", "(");
/* 四则运算符号 */
static List<String> primary = Arrays.asList("+", "-", "*", "/");
/* 根号 */
static List<String> middle1 = Arrays.asList("√", "");
/* 平方 */
static List<String> middle2 = Arrays.asList("^2", "");
/* 三角函数 */
static List<String> high = Arrays.asList("sin", "sin√", "cos", "", "cos√", "tan", "tan√");
/* 格式化时间 */
SimpleDateFormat problemName = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
成员属性包括写文件的 BufferedWriter对象、限定文件名时间格式的 SimpleDateFormat对象和各个难度的题目所涉及的符号。
/**
* problemWriter 写入题目
*
* @param bufferedWriter 读写对象
* @param problem 题目
*/
public void problemWriter(BufferedWriter bufferedWriter, String problem) {
...
}
/**
* problemClose 停止写入题目
*
* @param bufferedWriter 读写对象
*/
public void problemClose(BufferedWriter bufferedWriter) {
...
}
/**
* saveProblem 保存题目
*
* @param name 用户名
*/
public void saveProblem(String name) {
...}
}
上面三个方法完成对题目的写入和保存,其中 writer 的各项动作分得很清楚,好评。
/**
* primarySetting 随机出题--小学
*
* @param name 出题者的姓名
* @param problemNumber 题目数量
*/
public void primarySetting(String name, int problemNumber) {
saveProblem(name);
for (int i = 0; i < problemNumber; i++) {
StringBuilder problem = new StringBuilder(" ");
int exampleNumber = 0;
int flag = 0;
int operandNumber = 2 + new Random().nextInt(3); /* 操作数个数 */
List<String> operandArr = new ArrayList<>();
List<Integer> Locations = new ArrayList<>();
while (exampleNumber < operandNumber) {
exampleNumber += 1; /* 操作数计数 */
if (exampleNumber <= operandNumber - 2) {/* 左括号 */
if (0 == flag) {
String S = bracketsLeft.get(new Random().nextInt(5));
operandArr.add(S);
if ("(".equals(S)) {
flag = exampleNumber;
}
}
}
operandArr.add(String.valueOf(1 + new Random().nextInt(100)));
operandArr.add(primary.get(new Random().nextInt(4)));
if (exampleNumber == operandNumber) {
operandArr.add(String.valueOf(1 + new Random().nextInt(100)));
}
}
for (int j = 0; j < operandArr.size(); j++) {
if (primary.contains(operandArr.get(j))) {
Locations.add(j);
}
}
while (0 != flag) {
int locate = new Random().nextInt(Locations.size());
if (locate >= flag) {
operandArr.add(Locations.get(locate), ")");
flag = 0;
}
}
for (String s : operandArr) {
problem.append(s);
}
problemWriter(bufferedWriter, (i + 1) + "、" + problem);
}
problemClose(bufferedWriter);
}
/**
* middleSetting 随机出题--初中
*
* @param name 出题者的姓名
* @param problemNumber 题目数量
*/
public void middleSetting(String name, int problemNumber) {
...
}
/**
* highSetting 随机出题--高中
*
* @param name 出题者的姓名
* @param problemNumber 题目数量
*/
public void highSetting(String name, int problemNumber) {
...
}
这三个方法的基本逻辑是一样的,就只贴出一个实现。它们完成不同年级难度的随机出题:先利用 List生成操作数和运算符号的序列,再把它们组合成字符串,每出一道题就保存一道,出完所有题之后关闭写文件对象。
主类 AutomaticGenerator
成员属性:
/* userInformation 用户信息 */
static String userInformation;
/* userArr 用户信息数组 */
static String[] userArr;
/* typeUser 用户类型 */
static String typeUser;
/* currentUser 当前用户 */
static String currentUser;
/* 当前用户编号 */
static int currentUserID;
/* userGroup 用户列表 */
static List<Map<String, String>> userGroup;
/* USER_TYPES 教师类型 */
static final String[] USER_TYPES = {"小学", "初中", "高中"};
/* problemSetting 出题类实例 */
static ProblemSetting problemSetting;
/* problemNumber 题目数量 */
static int problemNumber;
/* allowIn 是否允许登录 */
boolean allowIn;
// 新建输入对象
Scanner in = new Scanner(System.in);
成员属性的注释很明了,就不多解释了。
/**
* exeInit 程序初始化
*/
public void exeInit() {
//新建用户对象
Users myUser = new Users();
//初始化用户对象
myUser.userInit();
//初始化用户组
userGroup = Arrays
.asList(myUser.getPrimary(), myUser.getMiddle(), myUser.getHigh(), myUser.getController());
}
/**
* exeInput 程序输入
*/
public void exeInput() {
/* 初始化每一次的输入 */
allowIn = false;
/* 输入用户信息 */
userInformation = in.nextLine();
/* 输入-1退出程序 */
if ("-1".equals(userInformation)) {
System.exit(0);
}
userArr = userInformation.split(" ");
}
一目了然,也不用多解释。
/**
* typeJudge 用户类型判断
*/
public void typeJudge(AutomaticGenerator automaticGenerator) {
...
}
typeJudge 方法检查密码正确性,输入了正确的密码就允许登录,判断用户是否是管理员并设置当前用户信息。
/**
* userMenu 用户菜单
*
* @param automaticGenerator 用户对象
*/
public void userMenu(AutomaticGenerator automaticGenerator) {
...
}
userMenu 负责普通用户的操作。除了出题和切换用户之外,还附加了修改密码和退出程序的功能。
/**
* controlMenu 管理员菜单
*
* @param automaticGenerator 管理员对象
*/
public void controlMenu(AutomaticGenerator automaticGenerator) {
...
}
controlMenu 负责管理员用户的操作。管理员可以查看所有用户、增删用户以及重置账户密码,也可以退出管理员模式重新登录。
/**
* main 主函数入口
*/
public static void main(String[] args) {
AutomaticGenerator automaticGenerator = new AutomaticGenerator();
System.out.print("请输入您的用户名和密码(以空格分隔,输入-1退出程序):");
automaticGenerator.exeInit();
automaticGenerator.exeInput();
automaticGenerator.typeJudge(automaticGenerator);
}
主函数就很简单了,新建 AutomaticGenerator 对象,进行系统初始化,然后读取输入、判断用户类型并等待操作。
优缺点分析
优点
- 对 Java 的应用能力很强,在我看来代码也很规范;
- 功能强大,不仅基本实现了项目原本的要求,还增添了新的实用功能,使得用户体验更加完整;
- 增加了管理员用户,管理用户数据更方便了;
- 类中成员的动作划分很清楚(前面也讲到了),类间联系和整个程序的逻辑结构较为清晰;
- 生成题目的步骤很有启发性,使用
List使得题目的“组装”方法十分简明。
缺点
- 各个年级的题目生成逻辑类似,相同的部分复用性不够好,既然在正式构建题目前使用
List先获取操作数和符号列表,不妨在高年级部分利用低年级题目的成果,在此基础上附加其他符号; - 似乎只能加一对括号(其实我也是)……
- 比较主观的见解:主类和用户类的部分功能划分得不太明显,用户类型属性没有在用户类中体现,而是相当于被直接放到了主类中。
嗯?杨少好像没有检查新题目和出过的题是否重复?XD
总结心得
这种分析搭档代码的学习形式确实裨益良多。一方面,我们都能学习对方代码中优秀的地方,另一方面,通过检查对方代码的不足,我们也都能反观自身的缺点,共同进步。
结对好耶

浙公网安备 33010602011771号