一个大气又可爱的标题
一、预估与实际
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
• Estimate | • 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | ||
• Analysis | • 需求分析 (包括学习新技术) | 50 | 100 |
• Design Spec | • 生成设计文档 | 20 | 30 |
• Design Review | • 设计复审 | 20 | 30 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 60 | 100 |
• Design | • 具体设计 | 60 | 100 |
• Coding | • 具体编码1 | 150 | 120 |
• Code Review | • 代码复审 | 60 | 60 |
• Test | • 测试(自我测试,修改代码,提交修改) | 100 | 150 |
Reporting | 报告 | ||
• Test Repor | • 测试报告 | 30 | 30 |
• Size Measurement | • 计算工作量 | 20 | 30 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 60 |
合计 | 720 |
二、需求分析
我通过查询小学教学大纲的方式了解到,小学一年级加减法有如下的几个特点:
-
加法有两种形式:
(1)个位数 + 个位数
(2)个位数 + 整十数 -
减法也有两种形式:
(1)整十数 - 个位数
(2)个位数 - 个位数 -
结果不能为负数
经过分析,我认为,这个程序应当:
- 结果不能为负数,也不能大于100
- 被减数必须大于减数
- 形如 A + B,A和B都为个位,或其中之一为整十数
- 形如 A - B ,A应该大于等于B
小学二年级乘除法有如下的几个特点:
- 乘法只有表内乘法
- 除法也是表内除法
- 除和商都是个位数
经过分析,我认为,这个程序应当:
- 除数不能为0
- 被除数应该大于或等于除数
- 积不能大于100
- 除和商都是个位数
三、设计
1. 设计思路
说明你如何设计这个程序
比如:
-
关键函数的流程图是怎样的?
-
算法的关键的关键是什么?
- 需要对用户的“错误输入”进行提醒,并结束程序。
- 需要根据不同的运算符号,制定不同的随机数生成规则。
2. 实现方案
写出具体实现的步骤
比如:
- 准备工作:先在Github上创建仓库,克隆到本地,在本地新建PSP1302文件夹
- 技术关键点:
- 思考应该采用哪种设计模式,这种模式是否便于后续的功能扩展和修改。
- 代码的结构该怎么构建
- 针对不同的运算符号,结合用户需求,制定不同的随机数生成规则
- 用正则表达式能更容易解决用户的错误输入
四、编码
请说明你如何按照设计思路进行编码,并记录你在开发中遇到的问题,与解决过程
1. 调试日志
记录编码调试的日志,请记录下开发过程中的 debug 历程
比如:
-
由于用户输入的参数是输入给主方法的args数组,则用户输入参数的个数不为2时,会抛异常。
解决方法:在主方法的开头新增对“args.length"的判断,若args不为“1”或“2”则提示用户输入错误。
-
除法运算中,因生成的除数为0,会抛异常。
解决方法:将代码“random.nextInt(10)”修改为“1+ random.nextInt(9)”,这样随机数生成的范围就从[0-9]变成了[1-9],就不会出现除数为0的情况
2. 关键代码
//RandomNumber是父类,调用工厂的静态方法,根据运算符号的不同,返回对应的子类对象
RandomNumber ran = RandomNumberFactory.createRan(bean.getSymbol());
ran.createRandomAandB(bean);
public class RandomNumberFactory {
public static RandomNumber createRan(String symbol) {
//根据不同的运算符号,返回对应的“随机数生成”子类
RandomNumber ran = null;
switch (symbol) {
case "+":
ran = new RandomOfAdd();
break;
case "-":
ran = new RandomOfSubtract();
break;
case "*":
ran = new RandomOfMultiply();
break;
case "/":
ran = new RandomOfDiv();
break;
}
return ran;
}
}
RandomNumber是父类,它有四个子类,分别是“加减乘除”的随机数生成类,有着各自的随机数生成规则。“RandomNumberFactory”是工厂类,该段代码运用了“简单工厂”的设计模式,工厂类可以根据运算符号的不同,实例化出合适的对象,通过多态,返回相对应的子类对象。
“简单工厂”的特点:专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
主要优点:将对象的创建过程进行了封装,用户不需要知道具体的创建过程,只需要调用工厂类获取对象即可。
3. 代码规范
请给出本次实验使用的代码规范:
- 第一条:不使用“Magic Number”,都用有意义的英文名进行变量的命名
- 第二条:不使用拼音进行命名
- 第三条:采用驼峰式命名规则,类名以大写字母开头,变量名和方法名以小写字母开头
- 第四条:代码中的命名均不能以下划线或美元符号开始,也不能一下划线或美元符号结束。
- 第五条:左小括号和字符之间不出现空格;同样的,有小括号和字符之间也不出现空格。
并人工检查代码是否符合规范
五、测试
测试用例 | 预期结果 | 实际结果 |
---|---|---|
dfsfh 1 | 输入有误,程序结束! | 同预期结果 |
10 a | 输入有误,程序结束! | 同预期结果 |
-123 2 | 输入有误,程序结束! | 同预期结果 |
001 2 | 输入有误,程序结束! | 同预期结果 |
34567 | 输入有误,程序结束! | 同预期结果 |
10 1 | 生成10道题的txt文本,并且是一年级的加减题 | 同预期结果 |
100 | 生成100道题的txt文本,并且是一年级的加减题 | 同预期结果 |
10 2 | 生成10道题的txt文本,并且是二年级的加减题 | 同预期结果 |
六、总结
请总结过程中的教训和经验,思考
-
是否使用了“软件开发的基本策略:分而治之”,是否需要重构
采用了分而治之的策略,基本做到了”一个类只实现一个功能“,将各个类的功能细分,这样对代码进行修改或者扩展时可能会相对容易些。
对于重构了解的不多,但本程序的中类分的较细,避免了过大的类和过长的方法,代码的耦合性较低,并把大部分的成员变量都存储在Bean类中,并且都设为private,一定程度上提高了封装性。
-
“高质量的设计、规范的编码以及有效的测试是保证软件产品质量的三个重要方面”,你是否采用了相关的手段,是否需要重构
-
采用了“简单工厂”,在于重构方面的理解还需要加强
-
在测试方面,对于用户的错误输入,应该给更详细的提示,告诉用户到底哪输入错了
-
在“高质量的设计”方面,测试了代码的运行时间,经测试,出一万道题(暂时撤销了题目数的限制)所花费的时间为2000ms,最后发现其实大部分时间都花在了字符串拼接上,因为我的代码中每生成一道题,就会执行一次
-
String s1 = s1 + “新增字符串”;
由于String带有final的关键字,所以程序把大部分时间浪费在字符串的创建和销毁上。当我用StringBuffer代替String后,发现出一万道题代码的总时长从2000多毫秒降低到了40毫秒,如此恐怖的效率提升确实让我震惊。如此清晰地认识到StringBuffer和String在“拼接字符串”上效率的差别也许就是我这次编程的最大收获。
-