2017-2018-2 20165315 实验二《Java面向对象程序设计》实验报告

一、实验内容及步骤

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

  • 单元测试

任务一:三种代码

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

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

TDD(测试驱动开发):

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

TDD的一般步骤如下:

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

基于TDD,可以有效避免过度开发的现象,因为我们只需要让测试通过即可。

测试类的创建:

  • 将鼠标放在主类类名上,点击出现的黄色小灯泡,在下拉选项中选择Create Test

  • 随后在Testing Library中的下拉选项中选择Junit 3,点击OK便可创建Test
  • 注意:不要忘记@Test的书写

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

测试结果:

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

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

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

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

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

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

  1. public class StringBufferDemo {
  2. StringBuffer buffer;
  3. public static char CharAt(StringBuffer buffer, int index) {
  4. return buffer.charAt(index);
  5. }
  6. public static int Capacity(StringBuffer buffer) {
  7. return buffer.capacity();
  8. }
  9. public static int IndexOf(StringBuffer buffer, String str) {
  10. return buffer.indexOf(str);
  11. }
  12. public static String ToString(StringBuffer buffer) {
  13. return "buffer = " + buffer.toString();
  14. }
  15. public static int Length(StringBuffer buffer) {
  16. return buffer.length();
  17. }
  18. }

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

测试代码如下:

  1. import junit.framework.TestCase;
  2. import org.junit.*;
  3. public class StringBufferDemoTest extends TestCase {
  4. StringBuffer buffer1 = new StringBuffer("iamastudent");
  5. StringBuffer buffer2 = new StringBuffer("youareastudent");
  6. StringBuffer buffer3 = new StringBuffer("heisateacher");
  7. @Test
  8. public void testCharAt() {
  9. assertEquals('i', StringBufferDemo.CharAt(buffer1, 0));
  10. assertEquals('o', StringBufferDemo.CharAt(buffer2, 1));
  11. assertEquals('r', StringBufferDemo.CharAt(buffer3, 11));
  12. }
  13. @Test
  14. public void testCapital() {
  15. assertEquals(27, StringBufferDemo.Capacity(buffer1));
  16. assertEquals(30, StringBufferDemo.Capacity(buffer2));
  17. assertEquals(28, StringBufferDemo.Capacity(buffer3));
  18. }
  19. @Test
  20. public void testLenght() throws Exception {
  21. assertEquals(11, StringBufferDemo.Length(buffer1));
  22. assertEquals(14, StringBufferDemo.Length(buffer2));
  23. assertEquals(12, StringBufferDemo.Length(buffer3));
  24. }
  25. @Test
  26. public void testIndexOf() {
  27. assertEquals(0, StringBufferDemo.IndexOf(buffer1, "iam"));
  28. assertEquals(-1, StringBufferDemo.IndexOf(buffer2, "You"));
  29. assertEquals(11, StringBufferDemo.IndexOf(buffer3, "r"));
  30. }
  31. @Test
  32. public void testToString() {
  33. assertEquals("buffer = iamastudent", StringBufferDemo.ToString(buffer1));
  34. assertEquals("buffer = youareastudent", StringBufferDemo.ToString(buffer2));
  35. assertEquals("buffer = heisateacher", StringBufferDemo.ToString(buffer3));
  36. }
  37. }

截图如下:

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)是封装的准确描述手段。

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

  • 点开StarUML程序后,在右下角的选项中选择Class选项建立一个类的UML图

  • 按照下图中提示分别创建对应的变量层和方法层

  • 在右下角选项中选择Generalization选项,创建类与类之间的调用关系

最终结果截图:

  • 较为简单:

  • 较为复杂:

3.设计模式

面向对象三要素是“封装、继承、多态”,任何面向对象编程语言都会在语法上支持这三要素。如何借助抽象思维用好三要素特别是多态还是非常困难的,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,依赖倒置原则)

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

OCP是OOD中最重要的一个原则,OCP的内容是:

软件实体(类,模块,函数等)应该对扩充开放,对修改封闭。

OCP可以用以下手段实现:

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

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

  1. abstract class Data{
  2. public abstract void DisplayValue();
  3. }
  4. class Integer extends Data {
  5. int value;
  6. Integer(){
  7. value=100;
  8. }
  9. public void DisplayValue(){
  10. System.out.println(value);
  11. }
  12. }
  13. class Document {
  14. Data pd;
  15. Document() {
  16. pd=new Integer();
  17. }
  18. public void DisplayData(){
  19. pd.DisplayValue();
  20. }
  21. }
  22. public class MyDoc {
  23. static Document d;
  24. public static void main(String[] args) {
  25. d = new Document();
  26. d.DisplayData();
  27. }
  28. }

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

产品代码:

  1. // Server Classes
  2. abstract class Data {
  3. abstract public void DisplayValue();
  4. }
  5. class Integer extends Data {
  6. int value;
  7. Integer() {
  8. value = 100;
  9. }
  10. public void DisplayValue() {
  11. System.out.println(value);
  12. }
  13. }
  14. class Long extends Data {
  15. long value;
  16. Long() {
  17. value = 111111111;
  18. }
  19. public void DisplayValue() {
  20. System.out.println(value);
  21. }
  22. }
  23. // Pattern Classes
  24. abstract class Factory {
  25. abstract public Data CreateDataObject();
  26. }
  27. class IntFactory extends Factory {
  28. public Data CreateDataObject() {
  29. return new Integer();
  30. }
  31. }
  32. class LongFactory extends Factory {
  33. public Data CreateDataObject() {
  34. return new Long();
  35. }
  36. }
  37. //Client classes
  38. class Document {
  39. Data pd;
  40. Document(Factory pf) {
  41. pd = pf.CreateDataObject();
  42. }
  43. public void DisplayData() {
  44. pd.DisplayValue();
  45. }
  46. }
  47. //Test class
  48. public class MyDoc {
  49. static Document d1, d2;
  50. public static void main(String[] args) {
  51. d1 = new Document(new IntFactory());
  52. d2 = new Document(new LongFactory());
  53. d1.DisplayData();
  54. d2.DisplayData();
  55. }
  56. }

运行结果截图:

4.练习

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

  • 伪代码
  1. // 定义属性并生成getter,setter
  2. double RealPart;
  3. double ImagePart;
  4. // 定义构造函数
  5. public Complex()
  6. public Complex(double R,double I)
  7. //Override Object
  8. public boolean equals(Object obj)
  9. public String toString()
  10. // 定义公有方法:加减乘除
  11. Complex ComplexAdd(Complex a)
  12. Complex ComplexSub(Complex a)
  13. Complex ComplexMulti(Complex a)
  14. Complex ComplexDiv(Complex a)
  • 产品代码
  1. public class Complex {
  2. // 定义属性并生成getter,setter
  3. private double RealPart;
  4. private double ImagePart;
  5. public double getterRealPart() {
  6. return this.RealPart;
  7. }
  8. public double getterImagePart() {
  9. return this.ImagePart;
  10. }
  11. public static double getterRealPart(double RealPart) {
  12. return RealPart;
  13. }
  14. public static double getterImagePart(double ImagePart) {
  15. return ImagePart;
  16. }
  17. public void setterRealPart(double RealPart) {
  18. this.RealPart = RealPart;
  19. }
  20. public void setterImagePart(double ImagePart) {
  21. this.ImagePart = ImagePart;
  22. }
  23. // 定义构造函数
  24. public Complex() {
  25. this.RealPart = 0;
  26. this.ImagePart = 0;
  27. }
  28. public Complex(double R, double I) {
  29. this.RealPart = R;
  30. this.ImagePart = I;
  31. }
  32. //Override Object
  33. public boolean equals(Object obj) {
  34. Complex complex = (Complex) obj;
  35. if (this == obj) {
  36. return true;
  37. } else if (!(obj instanceof Complex)) {
  38. return false;
  39. } else if (getterRealPart() != complex.getterRealPart()) {
  40. return false;
  41. } else if (getterImagePart() != complex.getterImagePart()) {
  42. return false;
  43. } else
  44. return true;
  45. }
  46. public String toString() {
  47. if (getterRealPart() == 0)
  48. return getterImagePart() + "i";
  49. else if (getterImagePart() == 0)
  50. return getterRealPart() + "";
  51. else if (getterImagePart() < 0)
  52. return getterRealPart() + "" + getterImagePart() + "i";
  53. else
  54. return getterRealPart() + "+" + getterImagePart() + "i";
  55. }
  56. // 定义公有方法:加减乘除
  57. Complex ComplexAdd(Complex a) {
  58. return new Complex(this.getterRealPart() + a.getterRealPart(), getterImagePart() + a.getterImagePart());
  59. }
  60. Complex ComplexSub(Complex a) {
  61. return new Complex(this.getterRealPart() - a.getterRealPart(), getterImagePart() - a.getterImagePart());
  62. }
  63. Complex ComplexMulti(Complex a) {
  64. return new Complex(this.getterRealPart() * a.getterRealPart() - a.getterImagePart() * this.getterImagePart(), a.getterImagePart() * this.getterRealPart() + a.getterRealPart() * this.getterImagePart());
  65. }
  66. Complex ComplexDiv(Complex a) {
  67. Complex c = new Complex();
  68. if (a.equals(c)) {
  69. System.out.println("错误,分母不能为零!");
  70. }
  71. return new Complex(this.getterRealPart() / a.getterRealPart(), this.getterImagePart() / a.getterImagePart());
  72. }
  73. }
  • 测试代码
  1. import junit.framework.TestCase;
  2. import org.junit.Test;
  3. public class ComplexTest extends TestCase {
  4. Complex complex1 = new Complex(3, 4);
  5. Complex complex2 = new Complex(1, -2);
  6. Complex complex3 = new Complex(1, 1);
  7. @Test
  8. public void testgetterRealPart() throws Exception {
  9. assertEquals(3.0, Complex.getterRealPart(3.0));
  10. assertEquals(1.0, Complex.getterRealPart(1.0));
  11. assertEquals(-2.0, Complex.getterRealPart(-2.0));
  12. }
  13. @Test
  14. public void testgetterImagePart() throws Exception {
  15. assertEquals(4.0, Complex.getterImagePart(4.0));
  16. assertEquals(-2.0, Complex.getterImagePart(-2.0));
  17. assertEquals(0.0, Complex.getterImagePart(0.0));
  18. }
  19. @Test
  20. public void testAdd() throws Exception {
  21. assertEquals("4.0+2.0i", complex1.ComplexAdd(complex2).toString());
  22. assertEquals("4.0+5.0i", complex1.ComplexAdd(complex3).toString());
  23. assertEquals("2.0-1.0i", complex2.ComplexAdd(complex3).toString());
  24. }
  25. @Test
  26. public void testSub() throws Exception {
  27. assertEquals("2.0+6.0i", complex1.ComplexSub(complex2).toString());
  28. assertEquals("2.0+3.0i", complex1.ComplexSub(complex3).toString());
  29. assertEquals("-3.0i", complex2.ComplexSub(complex3).toString());
  30. }
  31. @Test
  32. public void testMulti() throws Exception {
  33. assertEquals("11.0-2.0i", complex1.ComplexMulti(complex2).toString());
  34. assertEquals("-1.0+7.0i", complex1.ComplexMulti(complex3).toString());
  35. assertEquals("3.0-1.0i", complex2.ComplexMulti(complex3).toString());
  36. }
  37. @Test
  38. public void testDiv() throws Exception {
  39. assertEquals("3.0-2.0i", complex1.ComplexDiv(complex2).toString());
  40. assertEquals("3.0+4.0i", complex1.ComplexDiv(complex3).toString());
  41. assertEquals("1.0-2.0i", complex2.ComplexDiv(complex3).toString());
  42. }
  43. }

二、实验过程中遇到的问题及解决

  • 一开始以为Test类是自己手动在Test目录下创建,导致结果不会出现测试条

解决过程:

1.将鼠标放在主类类名上,点击出现的黄色小灯泡,在下拉选项中选择Create Test

2.随后在Testing Library中的下拉选项中选择Junit 3,点击OK便可创建Test

3.注意:不要忘记@Test的书写

  • 在书写测试类时,StringBuffer()中的capacity()方法总是出错

解决方法:

参考capacity()的用法可知,StringBuffer在内部维护一个字符数组,当你使用缺省的构造函数来创建StringBuffer对象的时候, StringBuffer的容量被初始化为16个字符,也就是说缺省容量就是16个字符。当StringBuffer达到最大容 量的时候,它会将自身容量增加到当前的2倍再加2,也就是(2*旧值+2)。

也就是说,对于空字符串,调用capacity()方法初始分配值为16;大小超过16时则扩充容量为34,再次扩充得到70。

三、实验体会与总结

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

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

四、PSP(Personal Software Process)时间

步骤 耗时 百分比
步骤 耗时 百分比
需求分析 20min 12%
设计 30min 18%
代码实现 50min 30%
测试 30min 18%
分析总结 40min 22%

五、代码链接

https://gitee.com/BESTI-IS-JAVA-2018/ch1/tree/master/20165315exp2

2017-2018-2 20165315 实验二《Java面向对象程序设计》实验报告的更多相关文章

  1. 实验二Java面向对象程序设计实验报告(2)

    实验二 Java面向对象程序设计 实验概述: 课程:程序设计与数据结构 班级:1623班 姓名: 邢天岳 学号:2309 指导老师:娄老师 王老师 实验日期:2017.4.16 实验名称: Java面 ...

  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. 20162311 实验二 Java面向对象程序设计 实验报告

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

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

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

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

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

  8. 20155217 实验二 Java面向对象程序设计 实验报告

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

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

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

  10. 20162305 实验二 Java面向对象程序设计 实验报告

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

随机推荐

  1. vue项目插入视频-mp4

    1. v.vue文件: <template> <div> <div class="contain"> <my-video :sources ...

  2. python小实例一:简单爬虫

    本文所谓的爬虫就是通过本地远程访问url,然后将url的读成源代码形式,然后对源代码进行解析,获取自己需要的数据,相当于简单数据挖掘.本文实现的是将一个网页的图片爬出保存到本地的过程,例子很简单,用的 ...

  3. 深入jUI(DWZ)

    -----------------------------------------------------------------------------主页面index.html <html& ...

  4. LINUX系统一一CentOS6.5之tomcat安装

    一 准备工作建立好文件夹 tomcat文件夹地址  二 下载并解压 当然是下载了 1.centos6.5系统里面下载 解压 2.本机下载 然后利用Xftp复制到目标文件加载解压(我用的是这种,随意啦) ...

  5. linux下搭建生成HLS所需的.ts和.m3u8文件

    要想利用HLS来实现视频的在线播放,就得需要将一个完整的视频文件切割成多个ts视频流,然后利用m3u8的索引文件来播放. 在Mac下,苹果提供了streamingTools的工具,里面有mediafi ...

  6. 修改maven项目的编译版本

    在pom.xml中添加如下代码 <build> <!-- 配置了很多插件 --> <plugins> <plugin> <groupId>o ...

  7. 吴裕雄 08-MySQL创建数据表

    MySQL 创建数据表创建MySQL数据表需要以下信息:表名表字段名定义每个表字段 语法以下为创建MySQL数据表的SQL通用语法:CREATE TABLE table_name (column_na ...

  8. 学习JS的心路历程-声明

    变量 在程序中将一个值指定(assign)给一个符号式的容器(symbolic container),叫做一个变量(variable). 声明在JS中目前提供了三种声明方式: var 声明一个变量,可 ...

  9. 什么是JIT,写的很好

    什么是JIT 一些其他解释的网站:http://www.sohu.com/a/169704040_464084 1.动态编译(dynamic compilation)指的是“在运行时进行编译”:与之相 ...

  10. Codeforces Round #499 (Div. 2) D. Rocket题解

    题目: http://codeforces.com/contest/1011/problem/D This is an interactive problem. Natasha is going to ...