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

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

一、实验内容及步骤

1.初步掌握单元测试和TDD

  • 单元测试

任务一:三种代码

用程序解决问题时,要学会写以下三种代码:

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

TDD(测试驱动开发):

  • 伪代码(思路)
  • 测试代码(产品预期功能)
  • 产品代码(实现预期功能)

TDD的一般步骤如下:

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

任务二:TDD(Test Driven Devlopment, 测试驱动开发)

老师在教程里给出的程序如下:

public static void main(String [] args){ StringBuffer buffer = new StringBuffer(); buffer.append('S'); buffer.append("tringBuffer"); System.out.println(buffer.charAt(1)); System.out.println(buffer.capacity()); System.out.println(buffer.length()); System.out.println(buffer.indexOf("tring")); System.out.println("buffer = " + buffer.toString());

对于这个程序,有五个方法需要测试,分别是:

  • charAt(int i):返回此序列中指定索引处的 char 值。第一个 char 值在索引 0 处,第二个在索引 1 处,依此类推,这类似于数组索引
  • capacity():返回当前容量。容量指可用于最新插入的字符的存储量,超过这一容量就需要再次进行分配
  • length():返回子浮窗的长度
  • indexOf(String s):返回输入的子字符串的第一个字母在母字符串的位置
  • toString(String s):返回此对象本身(它已经是一个字符串)

在产品代码里,我们需要为这五个方法加上返回值,并与我们的断言进行比较。产品代码如下:

public class StringBufferDemo {
    StringBuffer buffer;

    public static char CharAt(StringBuffer buffer, int index) { return buffer.charAt(index); } public static int Capacity(StringBuffer buffer) { return buffer.capacity(); } public static int IndexOf(StringBuffer buffer, String str) { return buffer.indexOf(str); } public static String ToString(StringBuffer buffer) { return "buffer = " + buffer.toString(); } public static int Length(StringBuffer buffer) { return buffer.length(); } }

根据上述该产品代码,写出对应的测试类,在测试类中我分别都使使用了3个例子来进行测试,如果出现问题,JUnit会出现红条,IDEA会提示哪一个测试用例出现问题,由此可以对应改正产品代码中的问题,直到JUnit出现绿条,任务完成。

测试代码如下:

import junit.framework.TestCase;
import org.junit.*;

public class StringBufferDemoTest extends TestCase { StringBuffer buffer1 = new StringBuffer("iamastudent"); StringBuffer buffer2 = new StringBuffer("youareastudent"); StringBuffer buffer3 = new StringBuffer("heisateacher"); @Test public void testCharAt() { assertEquals('i', StringBufferDemo.CharAt(buffer1, 0)); assertEquals('o', StringBufferDemo.CharAt(buffer2, 1)); assertEquals('r', StringBufferDemo.CharAt(buffer3, 11)); } @Test public void testCapital() { assertEquals(27, StringBufferDemo.Capacity(buffer1)); assertEquals(30, StringBufferDemo.Capacity(buffer2)); assertEquals(28, StringBufferDemo.Capacity(buffer3)); } @Test public void testLenght() throws Exception { assertEquals(11, StringBufferDemo.Length(buffer1)); assertEquals(14, StringBufferDemo.Length(buffer2)); assertEquals(12, StringBufferDemo.Length(buffer3)); } @Test public void testIndexOf() { assertEquals(0, StringBufferDemo.IndexOf(buffer1, "iam")); assertEquals(-1, StringBufferDemo.IndexOf(buffer2, "You")); assertEquals(11, StringBufferDemo.IndexOf(buffer3, "r")); } @Test public void testToString() { assertEquals("buffer = iamastudent", StringBufferDemo.ToString(buffer1)); assertEquals("buffer = youareastudent", StringBufferDemo.ToString(buffer2)); assertEquals("buffer = heisateacher", StringBufferDemo.ToString(buffer3)); } }

截图如下:

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

面向对象(Object-Oriented)的三要素包括:封装、继承、多态。面向对象的思想涉及到软件开发的各个方面,如面向对象分析(OOA)、面向对象设计(OOD)、面向对象编程实现(OOP)。OOA根据抽象关键的问题域来分解系统,关注是什么(what)。OOD是一种提供符号设计系统的面向对象的实现过程,用非常接近问题域术语的方法把系统构造成“现实世界”的对象,关注怎么做(how),通过模型来实现功能规范。OOP则在设计的基础上用编程语言(如Java)编码。贯穿OOA、OOD和OOP的主线正是抽象。

OOD中建模会用图形化的建模语言UML(Unified Modeling Language),UML是一种通用的建模语言。

过程抽象的结果是函数,数据抽象的结果是抽象数据类型(Abstract Data Type,ADT),类可以作具有继承和多态机制的ADT。数据抽象才是OOP的核心和起源。

OO三要素的第一个要素是封装,封装就是将数据与相关行为包装在一起以实现信息就隐藏,Java中用类进行封装。

封装实际上使用方法(method)将类的数据隐藏起来,控制用户对类的修改和访问数据的程度,从而带来模块化(Modularity)和信息隐藏(Information hiding)的好处;接口(interface)是封装的准确描述手段。

任务三:对MyDoc类进行扩充,让其支持Long类,初步理解设计模式

OCP是OOD中最重要的一个原则,OCP的内容是:
软件实体(类,模块,函数等)应该对扩充开放,对修改封闭。

OCP可以用以下手段实现:

  • 抽象和继承
  • 面向接口编程

老师给出的以Int型为例的代码如下:

abstract class Data{
    public abstract void DisplayValue(); } class Integer extends Data { int value; Integer(){ value=100; } public void DisplayValue(){ System.out.println(value); } } class Document { Data pd; Document() { pd=new Integer(); } public void DisplayData(){ pd.DisplayValue(); } } public class MyDoc { static Document d; public static void main(String[] args) { d = new Document(); d.DisplayData(); } }

在上述代码的基础上,要求系统支持Long类,这是一个合理的要求,要支持Long类,Document类要修改两个地方,这违反了OCP原则,使用多态可以解决部分问题:

产品代码:

// Server Classes 
abstract class Data { abstract public void DisplayValue(); } class Integer extends Data { int value; Integer() { value = 100; } public void DisplayValue() { System.out.println(value); } } class Long extends Data { long value; Long() { value = 20175315; } public void DisplayValue() { System.out.println(value); } } // Pattern Classes abstract class Factory { abstract public Data CreateDataObject(); } class IntFactory extends Factory { public Data CreateDataObject() { return new Integer(); } } class LongFactory extends Factory { public Data CreateDataObject() { return new Long(); } } //Client classes class Document { Data pd; Document(Factory pf) { pd = pf.CreateDataObject(); } public void DisplayData() { pd.DisplayValue(); } } //Test class public class MyDoc { static Document d1, d2; public static void main(String[] args) { d1 = new Document(new IntFactory()); d2 = new Document(new LongFactory()); d1.DisplayData(); d2.DisplayData(); } }

运行结果截图:

4.练习

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

  • 伪代码
// 定义属性并生成getter,setter
double RealPart;
double ImagePart;
// 定义构造函数
public Complex() public Complex(double R,double I) //Override Object public boolean equals(Object obj) public String toString() // 定义公有方法:加减乘除 Complex ComplexAdd(Complex a) Complex ComplexSub(Complex a) Complex ComplexMulti(Complex a) Complex ComplexDiv(Complex a)
  • 产品代码
public class Complex {
    // 定义属性并生成getter,setter private double RealPart; private double ImagePart; public double getterRealPart() { return this.RealPart; } public double getterImagePart() { return this.ImagePart; } public static double getterRealPart(double RealPart) { return RealPart; } public static double getterImagePart(double ImagePart) { return ImagePart; } public void setterRealPart(double RealPart) { this.RealPart = RealPart; } public void setterImagePart(double ImagePart) { this.ImagePart = ImagePart; } // 定义构造函数 public Complex() { this.RealPart = 0; this.ImagePart = 0; } public Complex(double R, double I) { this.RealPart = R; this.ImagePart = I; } //Override Object public boolean equals(Object obj) { Complex complex = (Complex) obj; if (this == obj) { return true; } else if (!(obj instanceof Complex)) { return false; } else if (getterRealPart() != complex.getterRealPart()) { return false; } else if (getterImagePart() != complex.getterImagePart()) { return false; } else return true; } public String toString() { if (getterRealPart() == 0) return getterImagePart() + "i"; else if (getterImagePart() == 0) return getterRealPart() + ""; else if (getterImagePart() < 0) return getterRealPart() + "" + getterImagePart() + "i"; else return getterRealPart() + "+" + getterImagePart() + "i"; } // 定义公有方法:加减乘除 Complex ComplexAdd(Complex a) { return new Complex(this.getterRealPart() + a.getterRealPart(), getterImagePart() + a.getterImagePart()); } Complex ComplexSub(Complex a) { return new Complex(this.getterRealPart() - a.getterRealPart(), getterImagePart() - a.getterImagePart()); } Complex ComplexMulti(Complex a) { return new Complex(this.getterRealPart() * a.getterRealPart() - a.getterImagePart() * this.getterImagePart(), a.getterImagePart() * this.getterRealPart() + a.getterRealPart() * this.getterImagePart()); } Complex ComplexDiv(Complex a) { Complex c = new Complex(); if (a.equals(c)) { System.out.println("错误,分母不能为零!"); } return new Complex(this.getterRealPart() / a.getterRealPart(), this.getterImagePart() / a.getterImagePart()); } }
  • 测试代码
import junit.framework.TestCase;
import org.junit.Test;

public class ComplexTest extends TestCase { Complex complex1 = new Complex(3, 4); Complex complex2 = new Complex(1, -2); Complex complex3 = new Complex(1, 1); @Test public void testgetterRealPart() throws Exception { assertEquals(3.0, Complex.getterRealPart(3.0)); assertEquals(1.0, Complex.getterRealPart(1.0)); assertEquals(-2.0, Complex.getterRealPart(-2.0)); } @Test public void testgetterImagePart() throws Exception { assertEquals(4.0, Complex.getterImagePart(4.0)); assertEquals(-2.0, Complex.getterImagePart(-2.0)); assertEquals(0.0, Complex.getterImagePart(0.0)); } @Test public void testAdd() throws Exception { assertEquals("4.0+2.0i", complex1.ComplexAdd(complex2).toString()); assertEquals("4.0+5.0i", complex1.ComplexAdd(complex3).toString()); assertEquals("2.0-1.0i", complex2.ComplexAdd(complex3).toString()); } @Test public void testSub() throws Exception { assertEquals("2.0+6.0i", complex1.ComplexSub(complex2).toString()); assertEquals("2.0+3.0i", complex1.ComplexSub(complex3).toString()); assertEquals("-3.0i", complex2.ComplexSub(complex3).toString()); } @Test public void testMulti() throws Exception { assertEquals("11.0-2.0i", complex1.ComplexMulti(complex2).toString()); assertEquals("-1.0+7.0i", complex1.ComplexMulti(complex3).toString()); assertEquals("3.0-1.0i", complex2.ComplexMulti(complex3).toString()); } @Test public void testDiv() throws Exception { assertEquals("3.0-2.0i", complex1.ComplexDiv(complex2).toString()); assertEquals("3.0+4.0i", complex1.ComplexDiv(complex3).toString()); assertEquals("1.0-2.0i", complex2.ComplexDiv(complex3).toString()); } }

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

最终结果截图:

 


二、实验过程中遇到的问题及解决
问题:在一开始的时候并不会使用Junit3进行测试
解决方法:通过百度以及室友提醒学会了如何使用

三、实验体会与总结

我通过本次实验学会了如何编写测试代码、如何绘画UML图以及在TDD模式下编写代码。

在自己上手实践操作过程中,加深了对平时不清楚的知识点的理解,也掌握了的Junit的用法。在使用测试代码的时候,既可以测试到代码是否正确,又规范了编程习惯,单元测试提供了一种高效快速的测试代码正确性的方法。

猜你喜欢

转载自www.cnblogs.com/cyygxy/p/10746825.html