20155322 2016-2017-2 《Java程序设计》实验二《Java面向对象程序设计》

20155322 2016-2017-2 《Java程序设计》实验二《Java面向对象程序设计》

实验目的与内容

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

  2. 理解并掌握面向对象三要素:封装、继承、多态

  3. 初步掌握UML建模

  4. 熟悉S.O.L.I.D原则

  5. 了解设计模式

实验知识点

  1. IDEA的git项目克隆与管理;

  2. IDEA的Junit插件下载与使用;

  3. TDD的方式研究学习StringBuffer;

  4. JDK帮助文档的使用。

  5. StarUML的使用。

实验步骤

任务一:学习了解伪代码、测试代码和产品代码

首先在编程之前我们要确认需要,需要实现什么功能?怎么实现?我认为这是一种很好的习惯,因为我之前总是一上来就哼哼哼的敲起了代码……那么需求如下:

我们要在一个MyUtil类中解决一个百分制成绩转成“优、良、中、及格、不及格”五级制成绩的功能。

根据这个需求,我们写出如下伪代码:

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

下面我们开始写产品代码,打开IDEA,新建项目Java_experiment_02_01,新建类MyUtil输入如下代码

public class MyUtil{
public static String percentage2fivegrade(int grade){
//如果成绩小于60,转成“不及格”
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 "优秀";
//其他,转成“错误”
else
return "错误";
}
}

下面是测试代码:

public class MyUtilTest {
public static void main(String[] args) {
// 百分制成绩是50时应该返回五级制的“不及格”
if(MyUtil.percentage2fivegrade(50) != "不及格")
System.out.println("test failed!");
else
System.out.println("test passed!");
}
}

这里虽然测试显示产品代码没有问题,但是我们需要注意只有一组输入测试是没有充分性的,不能全面的体现出产品代码的处理能力,所以我们需要多追加几组测试用例:


public class MyUtilTest {
public static void main(String[] args) {
//测试出错情况
if(MyUtil.percentage2fivegrade(-10) != "错误")
System.out.println("test failed 1!");
else if(MyUtil.percentage2fivegrade(115) != "错误")
System.out.println("test failed 2!");
//测试边界情况 else if(MyUtil.percentage2fivegrade(0) != "不及格")
System.out.println("test failed 1!");
else if(MyUtil.percentage2fivegrade(60) != "及格")
System.out.println("test failed 2!");
else if(MyUtil.percentage2fivegrade(70) != "中等")
System.out.println("test failed 3!");
else if(MyUtil.percentage2fivegrade(80) != "良好")
System.out.println("test failed 4!");
else if(MyUtil.percentage2fivegrade(90) != "优秀")
System.out.println("test failed 5!");
else if(MyUtil.percentage2fivegrade(100) != "优秀")
System.out.println("test failed 6!");
else
System.out.println("test passed!");
}
}
  • 这一次我们发现问题出来了,在进行边界测试的时候产品代码无法完整处理这些数据,所以我们需要进行一定的修改:
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 "错误";
}
}

如此,我们完成了一个较为完善的产品代码,老师通过这个指导告诉我们:

  • 好的代码一定要有测试代码
  • 测试用例的内容一定要全面,一般要求是测试代码比产品代码多

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

根据老师的博客和书上的课本,我了解到我们需要使用TDD这种方式去研究StringBuffer

  • StringBuffer对象是一个有内容的对象,一般形式如下:
StringBuffer s = new StringBuffer(“abc”);

这样初始化出的StringBuffer对象的内容就是字符串”abc”。StringBuffer类中的方法主要偏重于对于字符串的变化,例如追加、插入和删除等,这个也是StringBuffer和String类的主要区别。

由于我掌握的不是很全面,所以在阅读书上的代码和参考同学的代码后写下了如下代码来学习和研究StringBuffer,产品代码如下:

/**
* Created by 20155322 on 2017/4/21.
*/
public class StringBufferTest {
StringBuffer buffer = new StringBuffer();
public StringBufferTest(StringBuffer buffer){
this.buffer = buffer;
}
public Character charAt(int i){
return buffer.charAt(i);
}
public int capacity(){
return buffer.capacity();
}
public int length(){
return buffer.length();
}
public int indexOf(String buf) {
return buffer.indexOf(buf);
}
}

测试代码如下:

/**
* Created by 20155322 on 2017/4/21.
*/
import junit.framework.TestCase;
import org.junit.Test; public class StringBufferTestTest 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() throws Exception{
assertEquals('S',a.charAt(0));
assertEquals('g',a.charAt(5));
assertEquals('r',a.charAt(11));
}
@Test
public void testcapacity() throws Exception{
assertEquals(28,a.capacity());
assertEquals(40,b.capacity());
assertEquals(52,c.capacity());
}
@Test
public void testlength() throws Exception{
assertEquals(12,a.length());
assertEquals(24,b.length());
assertEquals(36,c.length());
}
@Test
public void testindexOf() throws Exception{
assertEquals(0,a.indexOf("Str"));
assertEquals(5,a.indexOf("gBu"));
assertEquals(10,a.indexOf("er"));
}
}

测试结果如下:

20155322 2016-2017-2 《Java程序设计》实验二《Java面向对象程序设计》

TDD方法让我对于代码的编写和设计有了新的理解,因为平时写代码都是根据需求写产品代码,然后再进行测试的,而TDD则将这个过程反了过来,通过测试区”驱动”代码的编写,在学会使用Junit这样的测试工具之后,感觉发现了一篇新大陆。

任务三:练习:让系统支持Float类,并在MyDoc类中添加测试代码表明添加正确,提交测试代码和运行结的截图,加上学号水印

  • 支持Float类其实只要加入一个新类就好。
  • 测试代码则需要加到主函数中。

下面是我的代码:

/**
* Created by 20155322 on 2017/4/21.
*/
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=1000000000
;
}
public void DisplayValue(){
System.out.println (value);
}
}*/
//上面搞错了,我应该是float……不是long
//这里是float
class Float extends Data {
float value;
Float() {
value = 1;
}
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 Float();
}
}
//Client classes
class Document {
Data pd;
Document(Factory pf){
pd = pf.CreateDataObject();
}
public void DisplayData(){
pd.DisplayValue();
}
}
//Test class **测试代码**
public class MyDoc {
static Document d;
public static void main(String[] args) {
d = new Document(new LongFactory());
d.DisplayData();
}
}

测试结果如下:

20155322 2016-2017-2 《Java程序设计》实验二《Java面向对象程序设计》

任务四:以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)

这个其实就是类似于一个简单的计数器:

/**
* Created by 20155322 on 2017/4/21.
*/
public class Complex{
private double r;
private double i; public Complex(double r, double i) {
this.r = r;
this.i = i;
} public static double getRealPart(double r) {
return r;
} public static double getImagePart(double i) {
return i;
} public Complex ComplexAdd(Complex c) {
return new Complex(r + c.r, i + c.i);
}
public Complex ComplexSub(Complex c) {
return new Complex(r - c.r, i - c.i);
}
public Complex ComplexMulti(Complex c) {
return new Complex(r * c.r - i * c.i, r * c.i + i * c.r);
}
public Complex ComplexDiv(Complex c) {
return new Complex((r * c.i + i * c.r)/(c.i * c.i + c.r * c.r), (i * c.i + r * c.r)/(c.i * c.i + c.r * c.r));
} public String toString() {
String s = " ";
if (i > 0)
s = r + "+" + i + "i";
if (i == 0)
s = r + "";
if (i < 0)
s = r + " " + i + "i";
return s;
}
}

下面进行测试:



/**
* Created by 20155322 on 2017/4/21.
*/
import junit.framework.TestCase;
import org.junit.Test; public class ComplexTest extends TestCase {
Complex c1 = new Complex(0, 3);
Complex c2 = new Complex(-1, -1);
Complex c3 = new Complex(2,1);
@Test
public void testgetRealPart() throws Exception {
assertEquals(-1.0, Complex.getRealPart(-1.0));
assertEquals(5.0, Complex.getRealPart(5.0));
assertEquals(0.0, Complex.getRealPart(0.0));
}
@Test
public void testgetImagePart() throws Exception {
assertEquals(-1.0, Complex.getImagePart(-1.0));
assertEquals(5.0, Complex.getImagePart(5.0));
assertEquals(0.0, Complex.getImagePart(0.0));
}
@Test
public void testComplexAdd() throws Exception {
assertEquals("-1.0+2.0i", c1.ComplexAdd(c2).toString());
assertEquals("2.0+4.0i", c1.ComplexAdd(c3).toString());
assertEquals("1.0", c2.ComplexAdd(c3).toString());
}
@Test
public void testComplexSub() throws Exception {
assertEquals("1.0+4.0i", c1.ComplexSub(c2).toString());
assertEquals("-2.0+2.0i", c1.ComplexSub(c3).toString());
assertEquals("-3.0 -2.0i", c2.ComplexSub(c3).toString());
}
@Test
public void testComplexMulti() throws Exception {
assertEquals("3.0 -3.0i", c1.ComplexMulti(c2).toString());
assertEquals("-3.0+6.0i", c1.ComplexMulti(c3).toString());
assertEquals("-1.0 -3.0i", c2.ComplexMulti(c3).toString());
}
@Test
public void testComplexComplexDiv() throws Exception {
assertEquals("-1.5 -1.5i", c1.ComplexDiv(c2).toString());
assertEquals("1.2+0.6i", c1.ComplexDiv(c3).toString());
assertEquals("-0.6 -0.6i", c2.ComplexDiv(c3).toString());
}
}

测试结果如下:

20155322 2016-2017-2 《Java程序设计》实验二《Java面向对象程序设计》

实验五:使用StarUML对实验二中的代码进行建模,发类图的截图,加上学号水印

主要是学习了使用StarUML这个软件,很方便,将类的关系直接体现了出来:

20155322 2016-2017-2 《Java程序设计》实验二《Java面向对象程序设计》

实验体会与总结

  • 这次实验内容任务较上一次难了不少,我花了大约6个小时才做完,我发现尽管是step-by-step的指导,但是还是会出现很多问题,例如Junit插件无法下载,或者下载之后无法加入到Dependencies中,或者是不同版本的IDEA的“Plugins”位置不同(如MAC版的IDEA在开始界面才能看到“Plugins”,我花在找这个选项上的时间都快有一个小时了)

  • 这次实验给我最大的收获是学习了Junit工具的使用以及TDD开发方式,收益颇丰,但是也有一点遗憾,就是花在学习使用工具的时间的太多了,真正学习的代码的时间并不多,或者说是因为找个选项都找半天而丧失了继续学习代码的兴致了…

PSP(Personal Software Process)时间

步骤 耗时(min) 百分比(%)
需求分析 30 8
设计 150 42
代码实现 70 19
测试 80 22
分析总结 30 8

参考资料

上一篇:为什么setinterval和settimeout越点击越快以及响应的解决办法


下一篇:netty-socketio整合springboot消息推送