實驗二 Java面向對象程序設計
實驗二 Java面向對象程序設計
實驗報告封面:
課程:Java程序設計 班級:1652班 姓名:王高源 學號:20165225
指導教師:婁嘉鵬 實驗日期:2018年4月16日
實驗時間:3:35 - 5:15 實驗序號:實驗二
實驗名稱:Java面向對象程序設計
實驗題目:
實驗內容:
1.初步掌握單元測試和TDD
2.理解並掌握面向對象三要素:封裝、繼承、多態
3.初步掌握UML建模
4.熟悉S.O.L.I.D原則
5.了解設計模式
實驗要求:
1.實現百分制成績轉成“優、良、中、及格、不及格”五級制成績的功能
實驗內容及步驟:
註:以下許多代碼都是老師給好的,僅稍作修改。
單元測試
TDD步驟:偽代碼(思路)→ 測試代碼(產品預期功能)→ 產品代碼(實現預期功能),這種開發方法叫“測試驅動開發”
偽代碼:偽代碼與具體編程語言無關,不要寫與具體編程語言語法相關的語句(如用malloc分配內存,這樣只能用C語言編程了),偽代碼從意圖層面來解決問題,最終,偽代碼是產品代碼最自然的、最好的註釋。
故根據實驗題目要求,我們應先寫出相應的“偽代碼”以便於我們完成編程,偽代碼如下:
百分制轉五分制: 如果成績小於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 "優秀"; //如果成績大於100,轉成“錯誤” 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!"); } }
運行結果如下:
成功了。
不過只測一組就是有偷懶嫌疑了,現在我們把一般情況都試一下:
也成功了
再看看異常情況下的測試:
這時我們發現了程序bug:判斷不及格時沒有要求成績大於零,於是我們修改源代碼,增加對負分的判斷:
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 "錯誤"; } }
這時候再重新測一下異常,發現通過了:
- 可喜可賀。
但是我們還沒有測試邊界情況,於是我們再對輸入為“0,60,70,80,90,100”這些邊界情況進行測試的代碼如下:
public class MyUtilTest { public static void main(String[] args) { //測試邊界情況 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 "錯誤"; } }
再測試一下:
成功了。
值得一提的是,Java中有單元測試工具JUnit來輔助進行TDD。
錯誤來的太突然:
按照老師給的步驟來走不小心就出現下圖,以為是網絡問題,結果換了網也沒有用:
- 然後試著什麽也不輸入直接點Browse repositories...,然後成功了。
- 接下來點擊綠色的那個安裝,又出錯了:
氣死了,再下一次,發現他又好了...
回到原題。
從網上下了一個everything用於查找junit.jar。
然後用老師給的新方法去測試了一下之前的代碼:
成功了,然後可能是版本問題,我的小綠條變成了小綠勾。
其實之前按照攻略來是失敗了的,不過還好有好看的助教學姐指點了我一下,才順利完成測試。
使用StarUML對實驗二中的代碼進行建模
面向對象(Object-Oriented)的三要素包括:封裝、繼承、多態。面向對象的思想涉及到軟件開發的各個方面,如面向對象分析(OOA)、面向對象設計(OOD)、面向對象編程實現(OOP)。OOA根據抽象關鍵的問題域來分解系統,關註是什麽(what)。OOD是一種提供符號設計系統的面向對象的實現過程,用非常接近問題域術語的方法把系統構造成“現實世界”的對象,關註怎麽做(how),通過模型來實現功能規範。OOP則在設計的基礎上用編程語言(如Java)編碼。貫穿OOA、OOD和OOP的主線正是抽象。
抽象:即“求同存異、去粗取精”的過程。將若幹事物中相同的部分進行剝離整理,並形成具有某特定功能的產品,這一過程即為抽象。過程抽象的結果是函數,數據抽象的結果是抽象數據類型其顯而易見的好處是(在程序設計中)減少了代碼大重復性,提高了效率。
封裝:將與某一將數據與相關行為包裝在一起以實現信息就隱藏,核心內容是模塊化和信息隱藏,與此相伴的是接口的使用。
封裝:
public class Dog { private String color; public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String bark(){ return "汪汪"; } public String toString(){ return "The Dog‘s color is " + this.getColor() +", and it shouts "+ this.bark() + "!"; } }
檢測:
public class DogTest { public static void main(String[] args) { Dog d = new Dog(); d.setColor("Yellow"); getInfo(d); } public static void getInfo(Dog d) { System.out.println(d.toString()); } }
下圖是我用starUML弄出來的建模(自學軟件真的很累哦):
以TDD的方式研究學習StringBuffer
- 關於StringBuffer的代碼如下:
public class StringBufferDemo {
public static void main(String [] args) {
StringBuffer buffer = new StringBuffer();
buffer.append(‘S‘);
buffer.append("tringBuffer");
System.out.println(buffer.charAt(i));
System.out.println(buffer.capacity());
System.out.println(buffer.length());
System.out.println(buffer.indexOf("tring"));
System.out.println("buffer = " + buffer.toString());
}
}
然而老師說這個代碼不可以直接用,在我把第七行的括號加上後,便完美運行了。
在老師給出的使用JUnit學習Java中,有四個需要我們去測試的東西:charAt()、capacity()、length()和indexOf。
基於以上,我們的測試代碼可為:
import junit.framework.TestCase; import org.junit.Test; 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(‘e‘,b.charAt(10)); assertEquals(‘f‘,c.charAt(20));; } @Test public void testcapacity() { assertEquals(28,a.capacity()); assertEquals(40,b.capacity()); assertEquals(52,c.capacity()); } @Test public void testindexOf() { assertEquals(0,a.indexOf("Str")); assertEquals(3,b.indexOf("ing")); assertEquals(10,c.indexOf("er")); } @Test public void testlength() { assertEquals(12,a.length()); assertEquals(24,b.length()); assertEquals(48,c.length()); } }
理所當然的我們測試一下:
- 成功了。
S.O.L.I.D原則
SRP(Single Responsibility Principle,單一職責原則)
對象提供單一職責的高度封裝,對象的改變僅僅依賴於單一職責的改變OCP(Open-Closed Principle,開放-封閉原則)
即對擴充開放(功能可增加),對修改封閉(源代碼不可改動)OCP實現手段:(1)抽象和繼承,(2)面向接口編程
LSP(Liskov Substitusion Principle,Liskov替換原則)
子類必須可以被其基類所代,父類型對象可以被子類型對象所取代
ISP(Interface Segregation Principle,接口分離原則)
客戶不應該依賴他們並未使用的接口DIP(Dependency Inversion Principle,依賴倒置原則)
示例如下(自己學號%6取余):
abstract class Data {
abstract public void DisplayValue();
}
class Integer extends Data {
int value;
Integer() {
value=100;
}
public void DisplayValue(){
System.out.println (value);
}
}
class Short extends Data {
short value;
Short() {
value = 5225;
}
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 ShortFactory extends Factory {
public Data CreateDataObject(){
return new Short();
}
}
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 ShortFactory());
d.DisplayData();
}
}
- 這是一個支持short類的代碼,關於學號取余,若想改成其他,只需要改一下這裏:
class ??? extends Data {
short value;
Short() {
value = 5225;
}
public void DisplayValue(){
System.out.println (value);
}
}
class ???Factory extends Factory {
public Data CreateDataObject(){
return new ???();
}
}
public class MyDoc {
static Document d;
public static void main(String[] args) {
d = new Document(new ???Factory());
d.DisplayData();
}
}
- 這樣一來代碼便符合了OCP原則,使代碼有良好的可擴充性、可維護性。
練習:使用TDD的方式設計關實現復數類Complex
- 偽代碼:
Complex類要輸出實部,輸出虛部,並按照a+bi的形式輸出復數。
Complex類中有兩個變量,實部RealPart和虛部ImaginePart;,
double RealPart;復數的實部
double ImagePart;復數的虛部
getRealPart():返回復數的實部
getImagePart();返回復數的虛部
setRealPart():設置復數的實部
setImagePart();設置復數的虛部
輸出形式:a+bi
(2)方法:
// 定義構造函數
public Complex()
public Complex(double R,double I)
// 定義公有方法:加減乘除
Complex ComplexAdd(Complex a) 加
Complex ComplexSub(Complex a) 減
Complex ComplexMulti(Complex a) 乘
Complex ComplexDiv(Complex a) 除
//Override Object
public boolean equals(Object obj)
public String toString():將復數輸出成a+bi的格式。
- 將其轉換成產品代碼:
import java.util.Scanner;
public class MyComplex {
static int r;
static int i;
private double m;
private double n;
public static int getRealPart(int RealPart){
r = RealPart;
return r;
}
public static int getImaginePart(int ImaginePart){
i = ImaginePart;
return i;
}
public MyComplex(double m, double n) {
this.m = m;
this.n = n;
}
public MyComplex add(MyComplex c) {
return new MyComplex(m + c.m, n + c.n);
}
public MyComplex minus(MyComplex c) {
return new MyComplex(m - c.m, n - c.n);
}
public MyComplex multiply(MyComplex c) {
return new MyComplex(m * c.m - n * c.n, m * c.n + n * c.m);
}
public String toString() {
String s = "";
if (n > 0)
s = "(" + m + "+" + n + "i" + ")";
if (n == 0)
s = "(" + m + ")";
if (n < 0)
s = "(" + m + n + "i" + ")";
return s;
}
}
- 測試代碼可為:
import org.junit.Test;
import static org.junit.Assert.*;
public class MyComplexTest {
MyComplex a=new MyComplex(1,2);
MyComplex b=new MyComplex(1,-4);
MyComplex c=new MyComplex(19,0);
MyComplex d=new MyComplex(0,-3);
MyComplex e=new MyComplex(0,0);
@Test
public void getRealPart() throws Exception {
assertEquals(1, MyComplex.getRealPart(1));
assertEquals(-1, MyComplex.getRealPart(-1));
assertEquals(5, MyComplex.getRealPart(5));
assertEquals(22, MyComplex.getRealPart(22));
assertEquals(-100, MyComplex.getRealPart(-100));
assertEquals(0, MyComplex.getRealPart(0));
}
@Test
public void getImaginePart() throws Exception {
assertEquals(1, MyComplex.getImaginePart(1));
assertEquals(-1, MyComplex.getImaginePart(-1));
assertEquals(5, MyComplex.getImaginePart(5));
assertEquals(22, MyComplex.getImaginePart(22));
assertEquals(-100, MyComplex.getImaginePart(-100));
assertEquals(0, MyComplex.getImaginePart(0));
}
@Test
public void add() throws Exception {
assertEquals("(2.0-2.0i)", a.add(b).toString());
assertEquals("(20.0+2.0i)", a.add(c).toString());
assertEquals("(1.0-1.0i)", a.add(d).toString());
assertEquals("(1.0+2.0i)", a.add(e).toString());
}
@Test
public void minus() throws Exception {
assertEquals("(0.0+6.0i)", a.minus(b).toString());
assertEquals("(-18.0+2.0i)", a.minus(c).toString());
assertEquals("(1.0+5.0i)", a.minus(d).toString());
assertEquals("(1.0+2.0i)", a.minus(e).toString());
}
@Test
public void multiply() throws Exception {
assertEquals("(9.0-2.0i)", a.multiply(b).toString());
assertEquals("(19.0+38.0i)", a.multiply(c).toString());
assertEquals("(6.0-3.0i)", a.multiply(d).toString());
assertEquals("(0.0)", a.multiply(e).toString());
}
}
- 測試成功:
實驗體會與總結
說實話,這次實驗要我編程我是不會編的..但是還好老師給了模板,只需要改一下就好了。
在實驗有了代碼支撐後,比較累的地方就是要看好多好多長博客,還有以上提到的一些軟件的操作方法你都要學(雖然老師有給出UML的另一個建模方法,但是我還是用了別的)。
值得一提的是,這次學到的東西能讓我更輕松的去修正代碼bug,減少了之後修復所需要的大量精力。
但是不管怎麽說,過程還是很累的,有時候都想把博客全部關掉,好好睡一下,好在最後我寫完了。
還是要繼續加油呀!
碼雲鏈接(這也是我第一次規範書寫git傳代碼哦)
實驗二 Java面向對象程序設計