结对编程
本次作业由 1759103 李思佳 与 1759107 陶彦婷 共同完成
经过一番讨论,我们决定用JAVA来完成这次作业。选择JAVA的原因大致可以分为以下三点:(1)我们学过的语言一共有三种:C、C++和JAVA。JAVA和C++都是面向对象语言,但C++为了照顾大量的C语言使用者,而兼容了C,这多少影响了其面向对象的彻底性。相比之下,JAVA则是完全的面向对象语言,它句法更清晰,规模更小,且编程环境更为简单。(2)这学期的课程安排偏向于使用JAVA编程,如:移动应用程序开发需要在Eclipse上编写Android程序。由于JAVA是上学期新学的语言,我们对于它的理解和熟悉度不如C/C++,正好可以借这个机会先写一个小程序再次熟悉一下JAVA的语法和编程环境。(3)Java技术正沿着网络渗入各个应用领域。自1995年5月正式推出的11年以来,Java已从编程语言发展成为全球第一大通用开发平台,其跨平台的技术优势为网络计算带来了划时代的变革。Java技术已为计算机行业主要公司所采纳,同时也被越来越多的国际技术标准化组织所接受。所以,学会用JAVA编程,对于我们来说是非常有必要的。
如果算上短学期程序设计基础实践那次的话,这是第二次结对编程,可以使用上次积累的些许经验。但是结对编程这个名词是第一次接触,为了更好的完成作业,我们去了解了一下这个名词——结对编程是一种敏捷软件开发的方法,两个程序员在一个计算机上共同工作。一个人输入代码,而另一个人审查他输入的每一行代码。结对编程能提供更好的设计质量和代码质量,两人合作能有更强的解决问题的能力。
于是我们找了一个空闲的下午,两个人坐在寝室里,模仿结对编程的场景开始我们本次的作业。刚拿到这道题的时候,李思佳第一想法就是一个多重循环列出所有表达式,然后随机选取300道题,但老师后面又给增加了需求。经过两个人初步的讨论,我们定下了大致的程序框架。李思佳读写代码的能力较强;遇到问题的时候善于分析,思路清晰;写代码之前会先想好算法。我写代码的能力比较弱,有时候思维会放不开。所以我们的分工是李思佳负责输入代码(驾驶员),我负责审查代码(观察员)。因为对JAVA语言的熟悉度不够,我们花了一整个下午,一边查资料、翻书一边写代码,才终于把程序大概的框架写完了,这时程序的基本功能已经可以实现。之后,我们又利用了几天的课余时间,对代码进行完善和优化。在这其间有想不出算法的苦恼,改bug时的迷茫和运行成功时的喜悦和自豪感,心情不可谓不复杂。
结对编程会使两个人都会发现自己平时没有注意到的地方。在进行个人编程的过程中,难免会有思维混乱的时候,也会有一些不熟悉的语法规则,而此时有一个人可以与你讨论或者一起查阅资料,对你编程中的一些小错误也会即时指出来,这样大大提高了编程的效率,也减少了代码的出错率,避免把问题拖到后面的阶段。因为每人在各自独立设计、实现软件的过程中不免要犯些错误,这是不可避免的。但在结对编程中,双方有随时的复审和交流,程序中的错误就会少得多,程序的初始质量会高很多,可以省下很多代码修改、测试的时间。此外,由于每人每天能够高效编程的时间是有限的,在写了一段时间代码后也会有累的时候,而两个人结对编程可以共同分担压力让每人都有适当的休息时间。
当然,在具体的实现过程中我们也遇到了许多问题,比如李思佳认为程序中的某个算法的实现用If语句来写比较清晰,而我认为用do-while语句更好;对于变量的定义是用字母还是可读性更高的单词……诸如此类的小问题在编程的过程中时有出现。遇到问题两个人会一起讨论,互相阐述自己的观点来说服对方,最后统一意见。值得注意的是,我们两个人在互相探讨的时候又出现了新的更好的思路。毕竟一个人思想有局限性,通过结对编程的方式,集两个人的想法于一起,这样才能设计出更加完善的程序。
以下为程序代码:
package 软工专题;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Scanner;
public class CalculateGenerator {
private static ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("JavaScript");
private static final char PLUS = '+';
private static final char MINUS = '-';
private static final char MULTIPLY = '*';
private static final char DIVIDE = '/';
private static final char DISPLAY_DIVIDE = '÷';
private static final char EQUALS = '=';
private static final String DOT = ".";
private static final String DOT_REGEX = "\\.";
private static final char[] operators = {
PLUS, MINUS, MULTIPLY, DIVIDE};private static final Integer MIN_OPERATOR_COUNT = 2;
private static final Integer MAX_OPERATOR_COUNT = 2;
private static final Integer MIN_CAL_VALUE = 0;
private static final Integer MAX_CAL_VALUE = 100;
private static final String FILE_NAME = "result.txt";
private static final String STUDENT_NO = "软工实践";
private static final String NEW_LINE = "\r\n";
private static Random random = new Random();
public static void main(String[] args) throws ScriptException {
System.out.println("请输入要生成的练习题数量:");
Scanner scanner = new Scanner(System.in);
Integer expressionCount = scanner.nextInt();
List<String> expressionList = new ArrayList<>();
for (int i = 1; i <= expressionCount; i++) {
expressionList.add(getNextExpression());
System.out.println(String.format("正在生成第 %s 道题", i));
}
writeExpressionsToFile(expressionList);
System.out.println("生成练习题完成");
}
/**
* 将练习题输出到文件
*
* @param expressionList
*/
private static void writeExpressionsToFile(List<String> expressionList) {
File file = new File(FILE_NAME);
try (FileWriter fileWriter = new FileWriter(file)) {
fileWriter.append(STUDENT_NO);
for (String expression : expressionList) {
fileWriter.append(NEW_LINE);
fileWriter.append(expression);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 生成练习题
*
* @return
* @throws ScriptException
*/
private static String getNextExpression() throws ScriptException {
System.out.println("尝试生成下一道题");
// 运算符数量
int operatorCount = random.nextInt(MAX_OPERATOR_COUNT + 1 - MIN_OPERATOR_COUNT) + MIN_OPERATOR_COUNT;
StringBuilder expression = new StringBuilder();
// 运算符
List<Character> operatorList = getOperatorList(operatorCount);
// 运算数
List<Integer> calValueList = getCalValueList(operatorCount + 1);
for (int i = 0; i < operatorList.size(); i++) {
Character operator = operatorList.get(i);
Integer previousCalValue = calValueList.get(i);
Integer nextCalValue = calValueList.get(i + 1);
expression.append(previousCalValue);
// 除法校验除数不为0且能被整除
if (DIVIDE == operator) {
calValueList.set(i + 1, getDivideCalValue(previousCalValue, nextCalValue));
} else if (MINUS == operator) {
// 减法校验被减数大于减数
// 当包含小数点时向下取整
String currentResultString = scriptEngine.eval(expression.toString()).toString();
if (currentResultString.contains(DOT)) {
currentResultString = currentResultString.split(DOT_REGEX)[0];
}
Integer currentResult = Integer.valueOf(currentResultString);
while (currentResult < nextCalValue) {
nextCalValue = random.nextInt(MAX_CAL_VALUE + 1);
}
calValueList.set(i + 1, nextCalValue);
}
expression.append(operator);
}
expression.append(calValueList.get(operatorCount));
// 计算当前结果是否为正整数
String result = scriptEngine.eval(expression.toString()).toString();
if (result.contains(DOT) || Integer.valueOf(result) < 0) {
System.out.println("当前题目不符合要求");
return getNextExpression();
}
else if( Integer.valueOf(result) >= 100 || Integer.valueOf(result) <= 0) {
System.out.println("当前题目不符合要求"); return getNextExpression(); }
String currentExpression = expression.append(EQUALS)
//.append(result)
.toString()
.replaceAll(String.valueOf(DIVIDE), String.valueOf(DISPLAY_DIVIDE));
return currentExpression;
}
/**
* 获取运算符
*
* @param operatorCount
* @return
*/
private static List<Character> getOperatorList(int operatorCount) {
List<Character> operatorList = new ArrayList<>();
for (int i = 1; i <= operatorCount; i++) {
Character operator = operators[random.nextInt(operators.length)];
operatorList.add(operator);
}
return operatorList;
}
/**
* 获取运算数
*
* @param calValueCount
* @return
*/
private static List<Integer> getCalValueList(int calValueCount) {
List<Integer> calValueList = new ArrayList<>();
for (int i = 0; i < calValueCount; i++) {
calValueList.add(random.nextInt(MAX_CAL_VALUE + 1));
}
return calValueList;
}
/**
* 当为除法时校验除数不为0且能被整除
*
* @param previousCalValue
* @param nextCalValue
* @return
*/
private static int getDivideCalValue(Integer previousCalValue, Integer nextCalValue) {
if (nextCalValue == 0 || previousCalValue % nextCalValue != 0) {
nextCalValue = random.nextInt(MAX_CAL_VALUE) + 1;
return getDivideCalValue(previousCalValue, nextCalValue);
}
return nextCalValue;
}
}
以下为程序类图:
以下为程序代码截图:
(1)输入要生成的练习题数量:300
(2)输出到result.txt文件中:(无答案版)
(3)增加输出答案的代码
(4)再次运行程序并打开result.txt文件,得到含答案版
此时,程序的功能基本完成,可以实现输入题目数量得到相应数量题目、避免题目重复等功能。
以下是输入5道题得到的result.txt文件:
但我们发现300到题内还是存在答案不在0-100范围内的题目,于是对代码进一步修改后:
得到的result.txt文件如下:
对这次作业的感触是,以前都是自己一个人写程序或者大作业,很少会有结对编程这种合作。结对编程不是为了让我们中某个人偷懒的,而是两个人一同进步的学习过程,互相帮助、学习和传递经验,可以得到能力上的互补。结对编程使一个人的知识两个人共享,双方在编程过程中的想法和意见,在互相沟通、交流之后能更快更有效地解决问题,的确不失为一种良好的软件开发的方法。