软件工程-第二次结对编程

1.题目要求

题目1:
  我们在刚开始上课的时候介绍过一个小学四则运算自动生成程序的例子,请实现它,要求:

  • 能够自动生成四则运算练习题
  • 可以定制题目数量
  • 用户可以选择运算符
  • 用户设置最大数(如十以内、百以内等)
  • 用户选择是否有括号、是否有小数
  • 用户选择输出方式(如输出到文件、打印机等)
  • 最好能提供图形用户界面(根据自己能力选做,以完成上述功能为主)

2.分工

  本次我的结对对象是王肖男,在作业的完成过程中,我们交换领航员和驾驶员的角色。本次作业的主要整体设计思路由我提出;在图形界面的部分,我作为领航员,王肖男作为驾驶员;在算式生成的部分,王肖男作为领航员,我作为驾驶员;在文件操作部分,我作为领航员,王肖男作为驾驶员。

3.我所负责的部分代码的思路

3.1算式生成部分

  在该部分,要根据传入的信息产生符合用户要求的四则运算式。
代码如下:

package ppp;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;

public class CreateList {

	private StringBuffer[] operator = new StringBuffer[4];
	private boolean createBrackets = true;//生成括号
	private boolean createMD = true;//生成乘除
	private boolean createDecimal = true;//是否包含小数
	private int maxNumber = 10;//最大值
	private ArrayList<StringBuffer> arrOperator;
	private ArrayList<StringBuffer> arrNumber;
	private ArrayList<StringBuffer> arrDemo;
	
	//"乘除","小数","括号","最大值"	
	public ArrayList<StringBuffer> create(String MD, String pointNum, String brackets,String maxNum) {
			if(pointNum == "no") 
				createDecimal = false;
			if(brackets == "no") 
				createBrackets = false;
			if(MD == "no") 
				createMD = false;
			maxNumber = Integer.parseInt(maxNum);
			operator[0] = new StringBuffer("+");
			operator[1] = new StringBuffer("-");
			operator[2] = new StringBuffer("*");
			operator[3] = new StringBuffer("/");
			arrOperator= new ArrayList<StringBuffer>();
			arrNumber = new ArrayList<StringBuffer>();
			arrDemo = new ArrayList<StringBuffer>();
		createOperator();
		if(createDecimal) 
			addDecimal();
		else
			addInteger();
		if(createBrackets)
			addBrackets();
		Merge();
		return arrDemo;
	}
	
	public void createOperator() {
		Random ra = new Random();
		int pointer = 0;
		int l = 4;
		if(!createMD) 
			l = 2;	
		int length = 2+(int)ra.nextInt(4);
		for(int i=0;i<length;++i) {
			pointer =ra.nextInt(l);
			if (i!=0&&operator[pointer]==arrOperator.get(i-1))
					i--;
			else
				arrOperator.add(operator[pointer]);
		}
	}
	
	public void addInteger() {
		int i=0;
		 Random ra = new Random();
		 int d = ra.nextInt(maxNumber);
		 arrNumber.add(new StringBuffer(Integer.toString(d)));
		for(StringBuffer sb:arrOperator) {
			int m = createInteger(i,sb);
			arrNumber.add(new StringBuffer(Integer.toString(m)));
			++i;
		}
	}
	
	public void addDecimal() {
		Random ra = new Random();
	    int d = ra.nextInt(maxNumber-1)+1;
	    arrNumber.add(new StringBuffer(Integer.toString(d)));
	    double flag = 0;
		for(StringBuffer sb:arrOperator) {
			flag = ra.nextDouble();
		    if(flag<0.7) {
				arrNumber.add(new StringBuffer(Integer.toString(ra.nextInt(maxNumber-1)+1)));
			 }else {
				 StringBuffer st = new StringBuffer();
				 st.append(ra.nextInt(maxNumber));
				 st.append('.');
				 st.append(ra.nextInt(maxNumber));
				 arrNumber.add(st);
			 }
		}
	}

	public void  Merge() {
		Iterator<StringBuffer> it1 = arrOperator.iterator();
		Iterator<StringBuffer> it2 = arrNumber.iterator();
	    for(;it1.hasNext();) {
	          arrDemo.add(it2.next());
	          arrDemo.add(it1.next());
	    }
	    arrDemo.add(it2.next());
	}
	
	private int createInteger(int i,StringBuffer sb) {
		 Random ra = new Random();
	    if(sb==operator[3]) {
	    	int num = Integer.parseInt(arrNumber.get(i).toString());
	    	if(num == 0)
	    		return ra.nextInt(maxNumber)+1;
			for(int j = num-1;j>0;--j) 
				if(num%j == 0)
					return j;	
	    }
	    return ra.nextInt(maxNumber)+1;
	}

	public void addBrackets() {
		int tempI;
		double tempD;
		int i = 0;
		boolean jump = false;
		for(StringBuffer sb:arrNumber) {
			tempD = Double.parseDouble(sb.toString());
			tempI = (int)tempD;
			if((tempD-tempI)<0.00000001&&(tempI>1)) {
				String s = String.valueOf(tempD);
				for(int t=0;t<s.length();++t)
					if(s.charAt(t)!='.') {	
						departInteger(i);
						jump = true;
						break;
					}
				if(jump)
					break;
			}
			++i;
		}
	}
	
	public void departInteger(int i) {
		Random ra = new Random();
		int l = 4;
		if(!createMD)
			l=2;
		int pointer = ra.nextInt(l);
		int number1 = 0;
		int number2 = 0;
		StringBuffer s = arrNumber.get(i);
		int element = Integer.parseInt((s.toString()));
		if(pointer == 0) {
			number1 = ra.nextInt(element);
			number2 = element - number1;
			StringBuffer newS = new StringBuffer("("+Integer.toString(number1)+"+"+Integer.toString(number2)+")");
			arrNumber.remove(i);
			arrNumber.add(i,newS);
		}else if(pointer == 2) {
			for(number1=element;number1>0;--number1) 
				if(element%number1 == 0) {
					number2 = element/number1;
					break;
				}
			StringBuffer newS = new StringBuffer("("+Integer.toString(number1)+"*"+Integer.toString(number2)+")");
			arrNumber.remove(i);
			arrNumber.add(i,newS);
		}else if(pointer == 3) {
			for(int j=maxNumber-1;j>=1;++j) 
				if(j%element==0) {
					number1 = j;
					break;
				}
			number2 = number1/element;
			StringBuffer newS = new StringBuffer("("+Integer.toString(number1)+"/"+Integer.toString(number2)+")");
			arrNumber.remove(i);
			arrNumber.add(i,newS);
		}
	}
}
	

3.2代码的编写思路

  1. 算式的各部分生成顺序:
      在多次尝试后,我采用了先生成运算符序列,再生成运算数序列,最后根据要求选择添加括号。这种方式能够解决在不产生小数且需要添加括号的要求下运算结果不包含小数的问题,根据数学运算的规则,先计算括号中的算式,在运算中全部为整数时,当括号及除法都不产生小数,整个算式的运算结果就不会产生小数。
  2. 容器选择:
      我选用的是ArrayList容器,用于存储运算符序列,运算数序列。在完成用户的算式生成要求后,再将两个序列合并为一个序列。而容器中存储的数据类型为StringBuffer,采用它的原因是它相对于String类型的可变性,能够更容易地实现我的括号的添加的方法。
  3. 算符产生函数
      将运算符生成为一个数组,依据随机数产生下标,组合成运算符序列。
  4. 生成小数
      由于随机生成的double类型尾数较多,所以我采用了分别生成整数部分与小数部分再进行拼接的方法。
  5. 添加括号
      该类中最复杂的部分是生成不含小数且含括号的算式。若是随机添加括号,则可能引起运算顺序改变而产生小数。所以在生成运算数序列时,将运算数序列的运算数分解为一个带括号的运算序列,这样在运算顺序改变时结果不会改变,即不产生小数。

3.3运行截图


4.代码地址

GitHub

5.总结

  在本次任务中由我负责整体的规划。在代码编写过程中不断改进,包括容器的选择,用户要求的实现顺序等,使我意识到软件工程思想的重要性,在过程中遇到的问题让我反思自己在整体规划方面能力的欠缺。在今后,我会在这方面努力加强自身。

6.界面类及文件操作类的代码审查表

计算器算法审查表
重要性 激活 级别 检查项
Y 20 命名规则是否与所采用的规范保持一致?
注释  
重要 Y 10 注释是否较清晰且必要?
重要 N 10 复杂的分支流程是否已经被注释?
N 10 距离较远的}是否已经被注释?
N 10 非通用变量是否全部被注释?
N 50 函数是否已经有文档注释?(功能、输入、返回及其他可选)
重要 Y 10 特殊用法是否被注释?
声明、空白、缩进  
Y 20 每行是否只声明了一个变量?(特别是那些可能出错的类型)
重要 Y 40 变量是否已经在定义的同时初始化?
重要 Y 40 类属性是否都执行了初始化?
Y 20 代码段落是否被合适地以空行分隔?
N 20 是否合理地使用了空格使程序更清晰?
Y 20 代码行长度是否在要求之内?
Y 20 折行是否恰当?
语句/功能分布/规模  
Y 20 包含复合语句的{}是否成对出现并符合规范?
N 20 是否给单个的循环、条件语句也加了{}?
Y 20 if/if-else/if-else if-else/do-while/switch-case语句的格式是否符合规范?
Y 40 单个变量是否只做单个用途?
重要 Y 20 单行是否只有单个功能?(不要使用;进行多行合并)
重要 Y 40 单个函数是否执行了单个功能并与其命名相符?
Y 20 操作符++和— —操作符的应用是否复合规范?
规模  
重要 Y 20 单个函数不超过规定行数?
重要 Y 100 缩进层数是否不超过规定?
重要 N 100 是否已经消除了所有警告?
重要 Y 40 常数变量是否声明为final?
重要 Y 80 对象使用前是否进行了检查?
重要 N 80 局部对象变量使用后是否被复位为NULL?
重要 Y 70 对数组的访问是否是安全的?(合法的index取值为[0, MAX_SIZE-1])。
重要 Y 20 是否确认没有同名变量局部重复定义问题?
Y 20 程序中是否只使用了简单的表达式?
重要 Y 20 所有判断是否都使用了(常量==变量)的形式?
重要 N 80 是否每个if-else if-else语句都有最后一个else以确保处理了全集?
重要 N 80 是否每个switch-case语句都有最后一个default以确保处理了全集?
Y 80 for循环是否都使用了包含下限不包含上限的形式?(k=0; k
重要 Y 40 XML标记书写是否完整,字符串的拼写是否正确?
N 40 对于流操作代码的异常捕获是否有finally操作以关闭流对象?
N 20 退出代码段时是否对临时对象做了释放处理?
可靠性(函数)  
重要 N 60 入口对象是否都被进行了判断不为空?
重要 N 60 入口数据的合法范围是否都被进行了判断?(尤其是数组)
重要 N 20 是否对有异常抛出的方法都执行了try...catch保护?
重要 Y 80 是否函数的所有分支都有返回值?
重要 N 50 int的返回值是否合理?(负值为失败,非负值成功)
N 20 对于反复进行了int返回值判断是否定义了函数来处理?
Y 60 关键代码是否做了捕获异常处理?
重要 N 60 是否确保函数返回CORBA对象的任何一个属性都不能为null?
重要 N 60 是否对方法返回值对象做了null检查,该返回值定义时是否被初始化?
重要 N 60 线程处理函数循环内部是否有异常捕获处理,防止线程抛出异常而退出?
N 20 原子操作代码异常中断,使用的相关外部变量是否恢复先前状态?
重要 N 100 函数对错误的处理是恰当的?
可维护性  
重要 Y 100 实现代码中是否消除了直接常量?(用于计数起点的简单常数例外)
Y 20 是否消除了导致结构模糊的连续赋值?(如a= (b=d+c ))
N 20 是否每个return前都要有日志记录?
N 20 是否有冗余判断语句?(如:if (b) return true; else return false;)
Y 20 是否把方法中的重复代码抽象成私有函数?
posted @ 2019-04-29 12:38  杨子昕  阅读(357)  评论(0编辑  收藏  举报