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的用法。在使用測試代碼的時候,既可以測試到代碼是否正確,又規范了編程習慣,單元測試提供了一種高效快速的測試代碼正確性的方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM