实验二《Java面向对象程序设计》

TDD与单元测试

前期准备:

什么是单元测试?

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如Java里单元指一个类。

正如想用程序解决问题时,如博客Intellj IDEA 简易教程所说,要会写三种码:

  • 伪代码
  • 产品代码
  • 测试代码

什么是TDD?

TDD(Test Driven Devlopment, 测试驱动开发)先写测试代码,然后再写产品代码的开发方法叫“测试驱动开发”(TDD)。

TDD的一般步骤如下:

  • 明确当前要完成的功能,记录成一个测试列表
  • 快速完成编写针对此功能的测试用例
  • 测试代码编译不通过(没产品代码呢)
  • 编写产品代码
  • 测试通过
  • 对代码进行重构,并保证测试通过(重构下次实验练习)
  • 循环完成所有功能的开发

Junit配置

步骤十分简单:下载junit.jar包并引入即可,但是由于不熟练还是遇到了很多的问题:

  • 第一次引入jar包之后误以为不需要再度引入:实际上每次需要运用TDD或者写测试类时都需重新引入。(数据库jar包同理)

  • 测试类名一定要以Test开头,否则会报错:

    在修改过后:

任务一:实现百分制成绩转成“优、良、中、及格、不及格”五级制成绩的功能

  • 伪代码:

    百分制转五分制:
    如果成绩小于60,转成“不及格”
    如果成绩在60与70之间,转成“及格”
    如果成绩在70与80之间,转成“中等”
    如果成绩在80与90之间,转成“良好”
    如果成绩在90与100之间,转成“优秀”
    其他,转成“错误”
  • 产品代码

    public class MyUtil{
    public static String percentage2fivegrade(int grade){
    //如果成绩小于0,转成“错误”
    if ((grade < 0))
    return "错误";
    //如果成绩小于60,转成“不及格”
    else if (grade < 60)
    return "不及格";
    //如果成绩在60与70之间,转成“及格”
    else if (grade < 70)
    return "及格";
    //如果成绩在70与80之间,转成“中等”
    else if (grade < 80)
    return "中等";
    //如果成绩在80与90之间,转成“良好”
    else if (grade < 90)
    return "良好";
    //如果成绩在90与100之间,转成“优秀”
    else if (grade <= 100)
    return "优秀";
    //如果成绩大于100,转成“错误”
    else
    return "错误";
    }
    }

  • 测试代码:需要兼顾正常情况异常情况边界情况

    import org.junit.Test;
    import junit.framework.TestCase;
    public class MyUtilTest extends TestCase {
    @Test
    public void testNormal() {//正常情况
    assertEquals("不及格", MyUtil.percentage2fivegrade(55));
    assertEquals("及格", MyUtil.percentage2fivegrade(65));
    assertEquals("中等", MyUtil.percentage2fivegrade(75));
    assertEquals("良好", MyUtil.percentage2fivegrade(85));
    assertEquals("优秀", MyUtil.percentage2fivegrade(95));
    }
    @Test
    public void testException(){//异常情况
    assertEquals("错误",MyUtil.percentage2fivegrade(-55));
    assertEquals("错误",MyUtil.percentage2fivegrade(105));
    }
    @Test
    public void testBoundary(){//边界情况
    assertEquals("不及格",MyUtil.percentage2fivegrade(0));
    assertEquals("及格",MyUtil.percentage2fivegrade(60));
    assertEquals("中等",MyUtil.percentage2fivegrade(70));
    assertEquals("良好",MyUtil.percentage2fivegrade(80));
    assertEquals("优秀",MyUtil.percentage2fivegrade(90));
    assertEquals("优秀",MyUtil.percentage2fivegrade(100));
    }
    }

测试代码运行截图:

任务二:以TDD的方式研究学习StringBuffer

  • 产品代码

    public class StringBufferDemo{
    StringBuffer str=new StringBuffer();
    StringBufferDemo(StringBuffer str){
    this.str=str;
    }
    public char charAt(int i){
    return str.charAt(i);
    }
    public int capacity(){
    return str.capacity();
    }
    public int length(){
    return str.length();
    }
    public int indexOf(String buf) {
    return str.indexOf(buf);
    }
    }
  • 测试代码

    import junit.framework.TestCase;
    import org.junit.Test; public class StringBufferDemoTest extends TestCase {
    StringBuffer a = new StringBuffer("StringBuffer");//测试12个字符(<=16)
    StringBuffer b = new StringBuffer("StringBufferStringBuffer");//测试24个字符(>16&&<=34)
    StringBuffer c = new StringBuffer("StringBufferStringBufferStringBuffer");//测试36个字符(>=34)
    @Test
    public void testcharAt(){
    assertEquals('S',a.charAt(0));
    assertEquals('B',a.charAt(6));
    assertEquals('r',a.charAt(11));
    }
    @Test
    public void capacityTest(){
    assertEquals(16,a.capacity());
    assertEquals(34,b.capacity());
    assertEquals(70,c.capacity());
    }
    @Test
    public void testlength(){
    assertEquals(12,a.length());
    assertEquals(24,b.length());
    assertEquals(36,c.length());
    }
    @Test
    public void testindexOf(){
    assertEquals(0,a.indexOf("Str"));
    assertEquals(6,a.indexOf("Buff"));
    assertEquals(10,a.indexOf("er"));
    }
    }
测试代码运行结果截图

思考题

老师在博客中《积极主动敲代码,使用JUnit学习Java》提出了思考题:

capacity()超过16个字符会再分配18个字符的空间。那超过34个字符呢?你猜猜capacity()的返回值(52?54?56?还是其他值)并修改上面的代码验证一下。

在自己测试之后感觉规律是:capacity*2+2,查询资料之后在博客关于StringBuff对象的capacitya方法返回值中得到了验证:

在上述测试代码与结果中也得到体现。

面向对象三要素:封装、继承、多态

S.O.L.I.D原则

  • SRP(Single Responsibility Principle,单一职责原则)
  • OCP(Open-Closed Principle,开放-封闭原则)
  • LSP(Liskov Substitusion Principle,Liskov替换原则)
  • ISP(Interface Segregation Principle,接口分离原则)
  • DIP(Dependency Inversion Principle,依赖倒置原则)

本次实验主要学习掌握的是:

OCP:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。(Softeware entities like classes,modules and functions should be open for extension but closed for modifications.)

DIP:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。(High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.)

任务三:对MyDoc类进行扩充,让其支持Float类

在仅实现Integer类时,UML图是正确的,但是一旦考虑到实现其他类的功能就会违反OCP原则

实验题目:让系统支持Float类,并在MyDoc类中添加测试代码表明添加正确

正确的UML如下:

  • 代码:
abstract class Data{//Data类:打印数据
abstract public void Display();
}
class Integer extends Data{//Data的子类
int value;
Integer(){
value=5310;
}
public void Display(){//方法重写
System.out.println(value);
}
}
class Float extends Data{//Data的子类
float value;
Float(){
value=165310.2f;
}
public void Display() {//方法重写
System.out.println(value);
}
}
abstract class Factory{
abstract public Data CreatDataObject();//抽象类:创造指定数据类型,所有数据类型的父类
}
class IntegerFactory extends Factory{//Integer类,Factory子类
public Data CreatDataObject(){//方法重写
return new Integer();
}
}
class FloatFactory extends Factory{//Byte类,Factory子类
public Data CreatDataObject(){//方法重写
return new Float();
}
}
class Documents{
Data pd;
Documents(Factory pf){
pd=pf.CreatDataObject();//使pd是数据类型的上转型对象
}
public void displayData(){//调用pd子类,即不同数据类型重写的对象
pd.Display();
}
}
public class MyDoc{
static Documents b;
public static void main(String[] args) {
b=new Documents(new FloatFactory());
b.displayData();
}
}
  • 运行结果截图:

  • 启发:

    在本次试验后我们进行的结对实验,在实现Language的语言选择功能时,学习模仿了本次的实验思想,实现了S.O.L.I.D原则。

任务四:以TDD的方式开发一个复数类Complex

回顾之前的实验过程,以TDD的方式进行编程

  • 伪代码:

    1)属性:复数包含实部和虚部两个部分,
    double RealPart;复数的实部
    double ImagePart;复数的虚部
    getRealPart():返回复数的实部
    getImagePart();返回复数的虚部
    setRealPart():设置复数的实部
    setImagePart();设置复数的虚部
    输出形式:a+bi
    (2)方法:
    ①定义构造函数
    public Complex()
    public Complex(double R,double I)
    ②定义公有方法:加减乘除
    Complex ComplexAdd(Complex a):实现复数加法
    Complex ComplexSub(Complex a):实现复数减法
    Complex ComplexMulti(Complex a):实现复数乘法
    Complex ComplexDiv(Complex a):实现复数除法
    ③Override Object
    public String toString():将计算结果转化为字符串形式并输出
  • 以TDD方式编写代码:

  • 测试代码:

    import junit.framework.TestCase;
    import org.junit.Test; public class ComplexTest extends TestCase {
    Complex a=new Complex(0,0);
    Complex b=new Complex(1,1);
    Complex c=new Complex(-1,-1);
    Complex d=new Complex(20.16,53.10);
    Complex e=new Complex(2,3);
    @Test
    public void testgetReal(){
    assertEquals(0.0,a.getRealPart());
    assertEquals(-1.0,c.getRealPart());
    assertEquals(20.16,d.getRealPart());
    }
    @Test
    public void testgetIma(){
    assertEquals(0.0,a.getImagePart());
    assertEquals(-1.0,c.getImagePart());
    assertEquals(53.1,d.getImagePart());
    }
    @Test
    public void testComAdd(){
    assertEquals("0.0",b.ComplexAdd(c).toString());
    assertEquals("1.0+i",a.ComplexAdd(b).toString());
    assertEquals("19.16+52.1i",c.ComplexAdd(d).toString());
    assertEquals("-1.0-i",a.ComplexAdd(c).toString());
    assertEquals("21.16+54.1i",b.ComplexAdd(d).toString());
    }
    @Test
    public void testComSub(){
    assertEquals("1.0+i",b.ComplexSub(a).toString());
    assertEquals("-21.16-54.1i",c.ComplexSub(d).toString());
    assertEquals("2.0+2.0i",b.ComplexSub(c).toString());
    }
    @Test
    public void testComMul(){
    assertEquals("0.0",a.ComplexMulti(d).toString());
    assertEquals("-1.0-i",b.ComplexMulti(c).toString());
    assertEquals("-20.16-53.1i",c.ComplexMulti(d).toString());
    assertEquals("40.32+159.3i",d.ComplexMulti(e).toString());
    }
    @Test
    public void testComDiv(){
    assertEquals("0.0",a.ComplexDiv(b).toString());
    assertEquals("-1.0-i",c.ComplexDiv(b).toString());
    assertEquals("-0.5-0.3333333333333333i",c.ComplexDiv(e).toString());
    assertEquals("10.08+17.7i",d.ComplexDiv(e).toString());
    }
    }
  • 产品代码:

    public class Complex {
    double a,b;
    Complex(double m,double n){//构造函数设置实部虚部
    a=m;
    b=n;
    }
    public double getRealPart(){//返回实部
    return a;
    } public double getImagePart() {//返回虚部
    return b;
    }
    public Complex ComplexAdd(Complex y){//加法
    double m=y.getRealPart();
    double n=y.getImagePart();
    double x=a+m;
    double z=b+n;
    return new Complex(x,z);
    }
    public Complex ComplexSub(Complex y){
    double m=y.getRealPart();
    double n=y.getImagePart();
    double x=a-m;
    double z=b-n;
    return new Complex(x,z);
    }
    public Complex ComplexMulti(Complex y){
    double m=y.getRealPart();
    double n=y.getImagePart();
    double x=a*m;
    double z=b*n;
    return new Complex(x,z);
    }
    public Complex ComplexDiv(Complex y){
    double m=y.getRealPart();
    double n=y.getImagePart();
    double x=a/m;
    double z=b/n;
    return new Complex(x,z);
    } @Override
    public java.lang.String toString() {
    String s="";
    if (a!=0&&b>0&&b!=1){
    s+= a+"+"+ b+"i";
    }
    else if(a!=0&&b==1){
    s+=a+"+i";
    }
    else if (a!=0&&b<0&&b!=-1){
    s+= a+""+b+"i";
    }
    else if (a!=0&&b==-1){
    s+=a+"-i";
    }
    else if (a!=0&&b==0){
    s+=a;
    }
    else if (a==0&&b!=0){
    s+=b+"i";
    }
    else if (a==0&&b==0){
    s+="0.0";
    }
    return s;
    }
    }
  • 实验运行结果:

任务五:使用StarUML对实验二中的代码进行建模

UML 的基本介绍

  • UML由3个要素构成:UML的基本构造块、支配这些构造块如何放置在一起的规则和运用于整个语言的公用机制。
  • UML有3种基本的构造块:事物、关系和图。
  • 事物是对模型中最具有代表性的成分的抽象,包括结构事物,如类(Class)、接口(Interface)、协作(Collaboration)、用例(UseCase)、主动类(ActiveClass)、组件(Component)和节点(Node);行为事物,如交互(Interaction)、态机(Statemachine)、分组事物(包,Package)、注释事物(注解,Note)。
  • 关系用来把事物结合在一起,包括依赖、关联、泛化和实现关系。

UML绘制

StarUML已经有For MAC版本,本次绘图就是通过StarUML完成的,本次实验中没有运用多个类的实验,所以以书上代码为示例绘制:

实验总结与体会

本次实验最大的收获是:

  • 技能

    • 学会使用Junit测试代码
    • 学会利用StarUML绘制UML
  • 元知识

    • 深刻理解学习了S.O.L.I.D原则,尤其是OCPDIP原则
    • 学会使用TDD编程方法

    PSP表格

    步骤 需求分析 百分比
    需求分析 20min 7.1%
    设计 20min 7.1%
    代码实现 80min 28.6%
    测试 80min 42.9%
    分析总结 40min 14.3%

20165310_Exp2实验二《Java面向对象程序设计》的更多相关文章

  1. 实验二 Java面向对象程序设计

    实验二 Java面向对象程序设计 实验内容 1. 初步掌握单元测试和TDD 2. 理解并掌握面向对象三要素:封装.继承.多态 3. 初步掌握UML建模 4. 熟悉S.O.L.I.D原则 5. 了解设计 ...

  2. 20145213《Java程序设计》实验二Java面向对象程序设计实验报告

    20145213<Java程序设计>实验二Java面向对象程序设计实验报告 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装,继承,多态 初步掌握UML建模 熟悉S.O. ...

  3. 20145206《Java程序设计》实验二Java面向对象程序设计实验报告

    20145206<Java程序设计>实验二Java面向对象程序设计实验报告 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O. ...

  4. 20145308刘昊阳 《Java程序设计》实验二 Java面向对象程序设计 实验报告

    20145308刘昊阳 <Java程序设计>实验二 Java面向对象程序设计 实验报告 实验名称 Java面向对象程序设计 实验内容 初步掌握单元测试和TDD 理解并掌握面相对象三要素:封 ...

  5. 20145113 实验二 Java面向对象程序设计

    20145113 实验二 Java面向对象程序设计 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 了解设计模式 1.初 ...

  6. JAVA课程实验报告 实验二 Java面向对象程序设计

    北京电子科技学院(BESTI) 实     验    报     告 课程:Java程序设计  班级:1353  姓名:韩玉琪  学号:20135317 成绩:             指导教师:娄嘉 ...

  7. 20145225唐振远 实验二 "Java面向对象程序设计"

    20145225<Java程序设计> 实验二 Java面向对象程序设计 实验报告 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S ...

  8. 20145208 实验二 Java面向对象程序设计

    20145208 实验二 Java面向对象程序设计 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 了解设计模式 实验步 ...

  9. 20162311 实验二 Java面向对象程序设计 实验报告

    实验二 Java面向对象程序设计 实验内容 1. 初步掌握单元测试和TDD 2. 理解并掌握面向对象三要素:封装.继承.多态 3. 初步掌握UML建模 4. 熟悉S.O.L.I.D原则 5. 了解设计 ...

  10. 20162317袁逸灏 第八周实验报告:实验二 Java面向对象程序设计

    20162317袁逸灏 第八周实验报告:实验二 Java面向对象程序设计 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 ...

随机推荐

  1. Pandas的concat方法

    在此我用的concat作用是加入新的记录,存储数据来用过的,不知道数据量大时候,效率会怎样 # 使用pandas来保存数据 df1 = pd.DataFrame([poem], columns=['p ...

  2. lua打包exe【转】

    可以使用srlua这个工具把lua脚本打包成exe,提供了lua 5.2.5.1.5.0的版本对应的源码. 不过我懒得编译了,发现“白的菜”替懒人编译好了(感谢),点击下载. 解压后出现“luapac ...

  3. dedecms如何调用当前栏目的子栏目及子栏目文章

    前面ytkah谈到了 dedecms调用当前栏目的子栏目怎么操作,有的朋友会问如果再增加一个调用子栏目文章的需求,即调用当前栏目的子栏目及子栏目文章,这个有办法实现吗?这时就要涉及到另外两个标签的调用 ...

  4. 为什么*p++等于*(p++)?

    你要先搞懂i++与++i的区别.i++是先赋值再自增,对于指针也是一样的.所以*p++是先取值,然后p再自增.加个括号还是一样的,*(p++)括号里面的内容还是p++,所以还是要先取值然后p再自增. ...

  5. visio 的使用方法

    1:往visio中添加商务图形和形状的方法.例如饼状图. 文件>形状>商务>图表和图形>绘制图表形状 2:visio 画半弧形,用铅笔 3: visio 画的图形,如果想要和v ...

  6. 阻止提交按钮的默认 action

    使用 preventDefault() 函数来阻止对表单的提交. 示例代码如下: <html><head><script type="text/javascri ...

  7. R中双表操作学习[转载]

    转自:https://www.jianshu.com/p/a7af4f6e50c3 1.原始数据 以上是原有的一个,再生成一个新的: > gene_exp_tidy2 <- data.fr ...

  8. java map.entry

    我希望要一个ArrayList<Entry>,类似C++中的pair, 但是Map.Entry是个接口,不能实例化,可以像下面这样写 HashMap<Integer, Integer ...

  9. CentOS7更改Docker默认镜像和容器存储位置

    图片出处:https://bobcares.com/wp-content/uploads/docker-change-directory.jpg 一.Why? 通常,当你开始使用docker时,我们并 ...

  10. [LeetCode] 589. N-ary Tree Preorder Traversal_Easy

    Given an n-ary tree, return the preorder traversal of its nodes' values. For example, given a 3-ary  ...