用程序解決問題時,要學會寫如下三種代碼:java
TDD的通常步驟以下:算法
基於TDD,能夠有效避免過分開發的現象,由於咱們只須要讓測試經過便可。編程
僞代碼:設計模式
百分制轉五分制: 若是成績小於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 "優秀"; else return "錯誤"; } }
測試代碼:app
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 testExceptions() { 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)); } }
測試結果:編程語言
老師所給程序以下:函數
public class StringBufferDemo{ 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.indexOf("tring")); System.out.println(buffer.length()); System.out.println("buffer = " + buffer.toString()); } }
根據TDD方式設計出JUnit測試用例:單元測試
charAt()
、capacity()
、length()
、indexOf()
。利用斷言進行比較。charAt()
返回此序列中指定索引處的 char 值。(第一個 char 值在索引 0 處,第二個在索引 1 處,依此類推,這相似於數組索引。);capacity()
是一個獲取當前實體的實際容量的方法;length()
是獲取實體中存放的字符序列的長度的方法;indexOf()
是返回輸入的子字符串的第一個字母在母字符串的位置的方法。因此在JUnit測試用例中設置三個容量與長度都不相同的字符串,來對capacity()
與length()
這兩個方法進行更加深入的學習。測試代碼:學習
import org.junit.Test; import junit.framework.TestCase; public class StringBufferDemoTest extends TestCase { StringBuffer a = new StringBuffer("StringBuffer"); StringBuffer b = new StringBuffer("StringBufferStringBuffer"); StringBuffer c = new StringBuffer("StringBufferStringBufferStringBuffer"); @Test public void testcharAt() { assertEquals('S',a.charAt(0)); assertEquals('i',a.charAt(3)); assertEquals('B',a.charAt(6)); } @Test public void testcapacity() { assertEquals(28,a.capacity()); assertEquals(40,b.capacity()); assertEquals(52,c.capacity()); } @Test public void testindexOf() { assertEquals(0,a.indexOf("String")); assertEquals(4,a.indexOf("ngB")); assertEquals(8,a.indexOf("ffer")); } @Test public void testlength() { assertEquals(12,a.length()); assertEquals(24,b.length()); assertEquals(36,c.length()); } }
測試結果:
抽象
- 抽象就是抽出事物的本質特徵而暫時不考慮他們的細節。對於複雜系統問題人們藉助分層次抽象的方法進行問題求解;在抽象的最高層,可使用問題環境的語言,以歸納的方式敘述問題的解。 - 編程原則DRY(Don't Repeat Yourself)
封裝、繼承與多態
- 接口(interface)是封裝的準確描述手段。 - 面向對象(Object-Oriented)的三要素包括:封裝、繼承、多態。面向對象的思想涉及到軟件開發的各個方面,如面向對象分析(OOA)、面向對象設計(OOD)、面向對象編程實現(OOP)。OOA根據抽象關鍵的問題域來分解系統,關注是什麼(what)。OOD是一種提供符號設計系統的面向對象的實現過程,用很是接近問題域術語的方法把系統構形成「現實世界」的對象,關注怎麼作(how),經過模型來實現功能規範。OOP則在設計的基礎上用編程語言(如Java)編碼。貫穿OOA、OOD和OOP的主線正是抽象。
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是OOD中最重要的一個原則,OCP的內容是:
- software entities (class, modules, function, etc.) should open for extension,but closed for modification.
- 軟件實體(類,模塊,函數等)應該對擴充開放,對修改封閉。
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
- 高層模塊不該該依賴於低層模塊。兩者都應該依賴於抽象抽象不該該依賴於細節。細節應該依賴於抽象
代碼實現:
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 { int value; Long(){ value=11271013; } public void DisplayValue(){ System.out.println(value); } } 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(); } } class Document { Data pd; Document(Factory pf){ pd = pf.CreateDataObject(); } public void DisplayData(){ pd.DisplayValue(); } } public class MyDoc{ static Document d; public static void main(String [] args) { d = new Document(new IntFactory()); d = new Document(new LongFactory()); d.DisplayData(); } }
測試結果:
代碼實現:
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 a) { return new Complex(r + a.r, i + a.i); } public Complex ComplexSub(Complex a) { return new Complex(r - a.r, i - a.i); } public Complex ComplexMulti(Complex a) { return new Complex(r * a.r - i * a.i, r * a.i + i * a.r); } public Complex ComplexDiv(Complex a) { return new Complex((r * a.i + i * a.r)/(a.i * a.i + a.r * a.r), (i * a.i + r * a.r)/(a.i * a.i + a.r * a.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; } }
測試代碼:
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()); } }
測試結果:
遇到的問題:
在寫JUnit測試用例時,尋找到用來測試的合適的複數比較困難。因爲複數的除法較爲複雜,在測試時,很容易出現無限循環的小數,致使測試失敗。
如圖:
本次實驗內容較多,可是掌握了JUnit測試的方法,經過編寫測試代碼,能夠測試本身代碼中正常狀況、異常狀況、邊界狀況或是程序中的算法是否正確。這樣減小了對於代碼的重複屢次運行與數據測試,費時也費力,有時一些數據會致使運行崩潰,對電腦也有損傷。
在利用JUnit測試代碼的過程當中,也掌握了對於assertEquals
的使用,對於代碼中一些方法的理解也更加透徹與深入。
在實驗過程當中,對於TDD方式、UML建模、S.O.L.I.D原則以及設計模式等等,也有更加深入而切身的體會。編寫代碼是一個須要思考與改進的過程,對本身Java的水平也是一個提升。
步驟 | 耗時 | 百分比 |
需求分析 | 10min | 17.2% |
設計 | 20min | 34.5% |
代碼實現 | 8min | 13.8% |
測試 | 15min | 25.9% |
分析總結 | 5min | 8.6% |