20175205 结对编程项目-四则运算 总结博客

20175205 20175306 结对编程项目-四则运算 总结博客

需求分析(描述自己对需求的理解,以及后续扩展的可能性)

  • 需求分析
    • 用户需求(往往不是真实需求)
    • 产品需求(对用户需求提炼分析)
  • 需求分析的步骤
    • 挖掘真实需求(需要透过现象看本质,挖掘真实需求)
      • 目标用户
      • 使用场景
      • 想要解决的问题
    • 提出解决方案(不只局限于用户的需求)
    • 筛选和验证方案

针对于本问题:

用户需求

  • 自动生成小学四则运算题目(加、减、乘、除);支持整数;支持多运算符(比如生成包含100个运算符的题目);支持真分数;统计正确率
  • 扩展需求:文件,处理生成题目并输出到文件,完成题目后从文件读入并判题;多语言支持:简体中文, 繁體中文, English;生成题目去重

    分析需求

  • 挖掘真实需求:
    • 目标用户:小学生
    • 使用场景:小学生进行数学有关四则运算题目的测试
    • 想要解决的问题:设计一个可以让学生测试的程序
  • 提出问题和解决方案:让用户可以进行数学测试,可以是数学四则运算测试,还可以是"开根号,幂运算"等等,还可以支持更多语言。

设计思路(同时输出UML类图)

上课讲了SOLID原则,反看第一周完成的代码,那都是个什么玩意,充分的满足了“低内聚,高耦合”,添加新要求的时候,一动都得动,无从下手,因此我决定重新修改,试着向SOLID原则靠拢,这样下次再添加什么需求的时候就不用全篇“大动”。

SOLID

  • 单一职责原则(高内聚):让一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类。
  • 开放封闭原则:对扩展是开放的,而对修改是封闭的。
  • 代替原则:当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系
  • 接口分离原则:不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。
  • 依赖倒置原则(低耦合)高层模块就不应该依赖于底层模块,二者都应该依赖于抽象模块

本周设计

  • 用户后期需求也许还会增加除了+、-、*、÷、()、/ 以外的符号比如需要开根号,求幂运算等等,因此我还增加了产生数学题目的抽象类
  • 增加真分数的情况,而且要添加真分数的运算,注意分母为零的情况
  • 针对于不同年龄段的学生,可能掌握的能力也不同,因此我对此也做了更改。比如,一年级能做加减运算;二年级能做乘除运算;三年级能做含括号的运算;其他年级就可做分数运算。
  • 处理生成全部题目并输出到文件,并从文件读入并判题
  • 多语言支持:简体中文, 繁體中文, English
  • 生成题目去重:经过分析,我认为就是运算符多于一个的题目,只要后缀表达式相同,那么就是重复的式子,因此我从这个角度进行了修改。但是后来和其他组同学讨论,发现这种方法并不全面,有很大的漏洞。。。

实现过程中的关键代码解释

import java.util.Locale;
import java.util.ResourceBundle;
public abstract class Language {
    public abstract void Print(String s);
}
public class Chinese extends Language{
    public Chinese(){}
    public void Print(String s){
        Locale locale1 = new Locale("zh","CN");
        ResourceBundle res1 = ResourceBundle.getBundle("zh_CN",locale1);
        System.out.print(res1.getString(s));
    }
}
public class English extends Language{
    public English(){}
    public void Print(String s) {
        Locale locale2 = new Locale("en", "US");
        ResourceBundle res2 = ResourceBundle.getBundle("en_US", locale2);
        System.out.print(res2.getString(s));
    }
}
public class TChinese extends Language {
    public TChinese(){}
    public void Print(String s){
        Locale locale3 = new Locale("zh","TW");
        ResourceBundle res3 = ResourceBundle.getBundle("zh_TW",locale3);
        System.out.print(res3.getString(s));
    }
}

注:此处代码是为了实现程序的多语言切换(国际化),软件实现国际化,需具备以下两个特征:
1、对于程序中固定使用的文本元素,例如菜单栏、导航条等中使用的文本元素、或错误提示信息,状态信息等,需要根据来访者的地区和国家,选择不同语言的文本为之服务。
2、对于程序动态产生的数据,例如(日期,货币等),软件应能根据当前所在的国家或地区的文化习惯进行显示。

if(s1.equals("1")&&(s2.equals("1")==false)){
            Numerator1 = 1;
            Denominator1 = 1;
            str2 = s2.split("/");
            Numerator2 = Integer.parseInt(str2[0]);
            Denominator2 = Integer.parseInt(str2[1]);
        }
        else if(s2.equals("1")&&(s1.equals("1")==false)){
            Numerator2 = 1;
            Denominator2 = 1;
            str1 = s1.split("/");
            Numerator1 = Integer.parseInt(str1[0]);
            Denominator1 = Integer.parseInt(str1[1]);
        }
        else if(s1.equals("1")&&s2.equals("1")){
            Numerator1 = 1;
            Denominator1 = 1;
            Numerator2 = 1;
            Denominator2 = 1;
        }
        else{
            if((s1.contains("/")==true)&&(s2.contains("/")==false)){
                str1 = s1.split("/");
                Numerator2 = Integer.parseInt(s2);
                Denominator2 = 1;
                Numerator1 = Integer.parseInt(str1[0]);
                Denominator1 = Integer.parseInt(str1[1]);
            }
            else if((s2.contains("/")==true)&&(s2.contains("/")==false)){
                Numerator1 = Integer.parseInt(s1);
                Denominator1 = 1;
                str2 = s2.split("/");
                Numerator2 = Integer.parseInt(str2[0]);
                Denominator2 = Integer.parseInt(str2[1]);
            }
            else if((s2.contains("/")==false)&&(s2.contains("/")==false)){
                Numerator1 = Integer.parseInt(s1);
                Denominator1 = 1;
                Numerator2 = Integer.parseInt(s2);
                Denominator2 = 1;
            }
            else{
                str1 = s1.split("/");
                str2 = s2.split("/");
                Numerator1 = Integer.parseInt(str1[0]);
                Denominator1 = Integer.parseInt(str1[1]);
                Numerator2 = Integer.parseInt(str2[0]);
                Denominator2 = Integer.parseInt(str2[1]);
            }
        }

这个部分是我一开始没有想到的情况,通过测试等手段发现了分母为零的情况,不是分数的情况。

测试方法

运行过程截图



代码托管

遇到的困难及解决方法

Q: 将带有÷号的题目输入文件,却出现了乱码。。。心态爆炸,本来好好的程序,加了个文件就输不出来了???
A:原来是系统的编码和程序的编码采用了不同的编码格式。在用Java程序进行读写含中文的txt文件时,经常会出现读出或写入的内容会出现乱码。通常,假如自己不修改的话,windows自身采用的编码格式是gbk(而gbk和gb2312基本上是一样的编码方式),而IDE中Encode不修改的话,默认是utf-8的编码,这就是为什么会出现乱码的原因。当在OS下手工创建并写入的txt文件(gbk),用程序直接去读(utf-8),就会乱码。为了避免可能的中文乱码问题,最好在文件写入和读出的时候显式指定编码格式。因此,我在输入读写文件上做了改动,表明了编码格式!!

try{
                    OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(file),"gbk");
                    BufferedWriter writer = new BufferedWriter(write);
                    writer.write(t);
                    writer.write(" = ");
                    writer.close();
                }
                catch (IOException e){}
                try{
                    InputStreamReader read = new InputStreamReader(new FileInputStream(file),"gbk");
                    BufferedReader reader = new BufferedReader(read);
                    System.out.print(reader.readLine());
                    read.close();
                }
                catch (IOException e){}

Q:多语言支持是什么鬼。。。就一直if-else??
A:Java中有ResourceBundle这样的类,可以做到

  • 轻松地本地化或翻译成不同的语言
  • 一次处理多个语言环境
  • 以后可以轻松进行修改,以便支持更多的语言环境
    这个类的作用就是读取资源属性文件(properties),然后根据.properties文件的名称信息(本地化信息),匹配当前系统的国别语言信息(也可以程序指定),然后获取相应的properties文件的内容。具体实施就是:
  • 在src目录下建后缀为properties的文件,我的理解是,可以一个语言体系建一个对应的读取资源属性文件
  • 文件里面放的内容是我想输出的字符串所对应的ASCII(可用ASCII转换器)
  • 我建了一个Language的类,里面包含3个方法,分别是中英繁体的实现;需要英文时,就调用实现英文的方法即可

Q:如何实现去重
A:我一开始的想法是,只要后缀表达式相同,那么就是相同的题目,但经过和伙伴讨论之后,又产生了新的想法,将每次进行运算的num1 op num2放入一个数组中,只要两个数组完全相同,那么就是相同的题目。(想法还在实施中)

对结对的小伙伴做出评价(重点指出需要改进的地方)

哇,小伙伴简直太重要了,我听了老师的各种原则之后,看我的程序简直是一团乱麻,剪不断理还乱,藕断丝连,你中有我,我中有你。。。因此我决定,按照SOLID原则及需求分析步骤,和我的小伙伴激烈讨论,给我提供了非常好的思路,重新整理了我的程序。我发现,在完成扩展内容的时候,就可以做到对外扩展,对内封闭,非常开心!和小伙伴的合作很顺利,我们分工明确,积极讨论,互相改错,相互进步,在此次结对学习过程中受益匪浅,不仅对知识点有了巩固和拓展,还对编程的思路方式等有了新的模式。

参考

Java读写txt文件时防止中文乱码问题出现的方法介绍
JAVA中ResourceBundle使用详解

PSP

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
• Estimate • 估计这个任务需要多少时间 600 900
Development 开发
• Analysis • 需求分析(包括学习新技术) 20 30
• Design Spec • 生成设计文档 20 30
• Design Review • 设计复审 20 30
• Coding Standard • 代码规范 (为目前的开发制定合适的规范) 20 40
• Design • 具体设计 30 40
• Coding • 具体编码 400 600
• Code Review • 代码复审 30 40
• Test • 测试(自我测试,修改代码,提交修改) 20 30
Reporting 报告
• Test Repor • 测试报告 10 20
• Size Measurement • 计算工作量 10 10
• Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 20 30
合计 600 900

总结学到的内容

这次做结对项目本着可以学到东西的目的,我对自己的代码的大致模型改了很多次,第一周自己尝试的时候虽然做出了符合要求的程序,但是内在关系还是非常杂乱的,完全不满足SOLID原则;在听了相关讲解之后,对设计模式,如何做需求分析,怎样写出高内聚低耦合的程序有了一定的了解,因此我努力的对自己的程序不断修改,努力完成一份符合设计原则的代码;在个过程中遇到了非常多的问题,解决完这个问题,又会产生下个问题等待优化,我不得不上网百度,和同学讨论,这样两个思想的碰撞产生的是无穷的力量,不仅增加了我的知识储备,还大大提高了自己的抗压能力,写完代码非常有成就感。

猜你喜欢

转载自www.cnblogs.com/orii/p/10636575.html