標籤:
20135101曹鈺晶實驗內容1. 初步掌握單元測試和TDD2. 理解並掌握物件導向三要素:封裝、繼承、多態3. 初步掌握UML建模4. 熟悉S.O.L.I.D原則5. 瞭解設計模式實驗要求1.沒有Linux基礎的同學建議先學習《Linux基礎入門(新版)》《Vim編輯器》 課程2.完成實驗、撰寫實驗報告,實驗報告以部落格方式發表在部落格園,注意實驗報告重點是
運行結果,遇到的
問題(工具尋找,安裝,使用,程式的編輯,調試,運行等)、
解決辦法(空洞的方法如“查網路”、“問同學”、“看書”等一律得0分)以及
分析(從中可以得到什麼啟示,有什麼收穫,教訓等)。報告可以參考範飛龍老師的指導3. 嚴禁抄襲,有該行為者實驗成績歸零,並附加其他懲罰措施。4.
請大家先在實驗樓中的~/Code目錄中用自己的學號建立一個目錄,代碼和UML圖要放到這個目錄中,中沒有學號的會要求重做,然後跟著下面的步驟練習。實驗步驟:(一)單元測試(1) 三種代碼
(1)三種代碼
虛擬碼
產品代碼
測試代碼
例:
需求:在一個MyUtil類中解決一個百分製成績轉成“優、良、中、及格、不及格”
五級製成績的功能。
先寫虛擬碼:
/*
百分制轉五分制:
如果成績小於60,轉成“不及格”;
如果成績在60與70之間,轉成“及格”
如果成績在70與80之間,轉成“中等”
如果成績在80 與90之間,轉成“良好”
如果成績在90與100之間,轉成“優秀”
其他,轉成“錯誤”
*/
產品代碼:
public class MyUtil {
public static String percentage2fivegrade(int grade){
//如果成績小於0,轉成“錯誤”
if (grade < 0)
return "錯誤";
//如果成績小於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 "優秀";
//如果成績大於100,轉成“錯誤”
else
return "錯誤";
//其他,轉成“錯誤”;
}
}
測試代碼:(針對MyUtil.java的測試模組)
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!");
}
}
測試案例(TestCase),是為某個特殊目標而編製的一組測試輸入、執行條件以及預期結果,以便測試某個程式路徑或核實是否滿足某個特定需求。
(2)TDD(Test Driven Development,測試驅動開發)
先寫測試代碼,再寫產品代碼
一般步驟:
明確當前要完成的功能,記錄一個測試清單;
快速完成編寫針對此功能的測試案例;
測試代碼編譯不通過(沒產品代碼);
編寫產品代碼;
測試通過;
對代碼進行重構,並保證測試通過(重構下次實驗練習);
迴圈完成所有功能的開發;
TDD的目標:Clean Code That Works.
TDD的slogan是:Keep the bar green,to keep the code clean.
TDD的編碼節奏是:
增加測試代碼,JUnit出現紅條;
修改產品代碼;
JUnit出現綠條,任務完成;
開啟Eclipse,單擊File->New->Javajava Project 建立一個TDDDemo的項目
在TDDDemo項目中,按右鍵TDDDemo,New->Source Folder 建立一個測試目錄test
按右鍵test, New->JUnit Test Case,建立一個測試案例類MyUtilTest
增加第一個測試案例testNormal,注意測試案例前一定要有註解@Test。
測試結果為紅條(red bar),測試有誤;
測試結果為綠條(green bar),測試通過。
(二)物件導向三要素
(1)抽象
去粗取精,化繁為簡,由表及裡,異中求同
複雜系統問題——分層次抽象
抽象最高層,使用問題環境的語言,以概括方式敘述問題的解;
抽象的較低層,採用過程化的方式描述;
抽象包括兩個方面:過程抽象 資料抽象
(2)封裝、繼承與多態
物件導向(Object-Oriented)三要素:封裝、繼承、多態。
物件導向分析(OOA)根據抽象關鍵的問題域來分解系統;
物件導向設計(OOD)提供符號設計系統的物件導向的實現過程,通過模型來實現功能規格
物件導向編程實現(OOP)在設計的基礎上用程式設計語言編碼。
OOD中建模用圖形化的建模語言UML(Unified Modeling Language),Windows中推薦使用StarUML。
過程抽象的結果是函數,資料抽象的結果的抽象資料類型(AbstractData Type,ADT),類可以作具有繼承和多態機制的ADT。資料抽象才算OOP的核心和起源。
封裝 —— 將資料與相關行為封裝在一起以實現資訊隱藏。Java中用類進行封裝。
封裝實際上使用方法(method)將類的資料隱藏起來,控制使用者對類的修改和訪問資料的 程度,從而帶來模組化(Modularity)和資訊隱藏(Information hiding)的好處;
介面(interface)是封裝的準確描述手段。
在實驗樓的環境中開啟shell,在命令列中輸入umbrello,開啟UML建模軟體umbrello
先單擊工具列上的類表徵圖,再在class diagram(類圖)中單擊一下,會彈出一個聖誕框,輸入類名Dog。
把滑鼠放到Dog類上,單擊右鍵,選擇Properties,在彈出的對話方塊中的Display中去掉Public Only選項,把滑鼠放到Dog類上,單擊右鍵,選擇New->Attribute,在彈出的對話方塊中的填好Type,Name,並選好Visibility。把滑鼠放到Dog類上,單擊右鍵,選擇New->Operation,在彈出的對話方塊中的填好Type,Name,並選好Visibility。
在UML 裡,一個類的屬效能顯示它的名字,類型,初始化值,屬性也可以顯示private,public,protected。 類的方法能顯示它們的方法名,參數,傳回型別,以及方法的private,public,protected屬性。其中:
- +表示public
- #表示 protected
- -表示 private
請大家注意UML類圖中繼承的標記法,是用一個帶三角的直線指向父類,通過繼承,我們消除了Dog類和Cat類中的重複代碼,符合DRY的要求。 繼承指一個類的定義可以基於另外一個已經存在的類,即子類基於父類,從而實現父類代碼的重用。既存類稱作基類、超類、父類(base class、super class、parent class),新類稱作衍生類別、繼承類、子類(derived class、inherited class、child class)。繼承關係表達了”Is a kind of“的關係,稱為“ISA”關係。繼承的關鍵在於確認子類為父類的一個特殊類型 。繼承是實現軟體可重用的根基,是提高軟體系統的可擴充性與可維護性的主要途徑。 如上面所示,以封裝為基礎,繼承可以實現代碼複用,需要注意的是,繼承更重要的作用是實現多態。 物件導向中允許不同類的對象對同一訊息做出響應,即同一訊息可以根據發送對象的不同而採用多種不同的行為方式,我們稱此現象為多態性。Java中,多態是指不同的類對象調用同一個簽名的成員方法時將執行不同代碼的現象。多態是物件導向程式設計的靈活性和可擴充性的基礎。
測試代碼:
(三)設計模式初步
(1)S.O.L.I.D原則
物件導向三要素是“封裝、繼承、多態”,任何物件導向程式設計語言都會在文法上支援這三要素。如何藉助抽象思維用好三要素特別是多態還是非常困難的,S.O.L.I.D類設計原則是一個很好的指導:
對擴充開放(Open For Extension )要求軟體模組的行為必須是可以擴充的,在應用需求改變或需要滿足新的應用需求時,我們要讓模組以不同的方式工作; 對修改封閉(Closed for Modification )要求模組的原始碼是不可改動的,任何人都不許修改已有模組的原始碼。 基於OCP,利用物件導向中的多態性(Polymorphic),更靈活地處理變更擁抱變化,OCP可以用以下手段實現:(1)抽象和繼承,(2)面向介面編程。 比如,在一個圖形系統中,已經存在三個模組Shape,Square,Circle,如所示:
使用者現大需要一個Triangle模組是一個合理的要求,由於我們使用了多態,原先的模組不需要改變,只要新增加一個模組Triangle就可以了。
SRP的內容是:There should never be more than one reason for a class to change.
對象提供單一職責的高度封裝,對象的改變僅僅依賴於單一職責的改變,它基於軟體設計中的高內聚性定義。
LSP的內容:
LSP的核心思想是父類型對象可以被子類型對象所取代。
LSP原則清楚地指出,OOD中“ISA關係”是就行為功能而言。行為功能(behavior)不是內在的、私人的,而是外在、公開的,是客戶程式所依賴的介面。
ISP的內容:Clients should not be forced to depend upin interfaces that they do not use.
首先要理解客戶是什麼,前面講到一個XXXX類,要有一個配套的XXXXTest類,XXXXTest中要使用XXXX類,XXXXTest類就可以看作XXXX類的客戶.
介面RowSetManager的功能過多,我們應該把這個介面分成多個介面,以便利於複用.
DIP的內容:
通過介面或者抽象類別,DIP在應用中通過依賴注入的方式實現解耦,重用低級模組,重用實現,解除依賴。 我們看一個例子:設想一個簡單的程式,其任務就是實現將鍵盤輸入的字元拷貝到印表機上。進一步假設實現平台中的作業系統並不支援裝置無關性。
上層函數copy依賴下層函數read,write。 DIP改變了依賴的方向,要求下面依賴上面,我們使用抽象類別實現DIP。
(2)模式與設計模式
模式是某外在環境(Context) 下﹐對特定問題(Problem)的慣用解決之道(Solution)。模式必須使得問題明晰,闡明為什麼用它來求解問題,以及在什麼情況下有用,什麼情況下不能起作用,每個模式因其重複性從而可被複用,本身有自己的名字,有可傳授性,能移植到不同情景下。模式可以看作對一個問題可複用的專家級解決方案。
這裡面最重要的是設計模式,在物件導向中設計模式的地位可以和面向過程編程中的資料結構的地位相當。
(3)設計模式實樣本
設計模式(design pattern)提供一個用於細化軟體系統的子系統或組件,或它們之間的關係圖,它描述通訊組件的公用再現結構,通訊組件可以解決特定語境中的一個設計問題。
隨著系統中對象的數量增多,對象之間的互動成指數增長,設計模式可以幫我們以最好的方式來設計系統。設計模式背後是抽象和SOLID原則。 設計模式有四個基本要素:
除SOLID原則外還有很多其它的物件導向原則。
(四)練習1使用TDD的方式設計關實現複數類Complex。2.實驗報告中統計自己的PSP(Personal Software Process)時間
步驟 |
耗時 |
百分比 |
需求分析 |
10min |
13.3% |
設計 |
15min |
20% |
代碼實現 |
20min |
26.7% |
測試 |
20min |
26.7% |
分析總結 |
10min |
13.3% |
虛擬碼:
1)複數類ComplexNumber的屬性
realPart: 實部,代表複數的實數部分
imaginPart: 虛部,代表複數的虛數部分
2)複數類ComplexNumber的方法
ComplexNumber() 建構函式,將實部,虛部都置為0
ComplexNumber(double realPart, double imaginPart) 建構函式,建立複數對象的同時完成複數的實部,虛部的初始化
getRealPart() 擷取實部
getImaginaryPart() 擷取虛部
getRealPart(double realPart) 設定實部
getImaginaryPart(double imaginPart) 設定虛部
add(ComplexNumber c) 複數相加
add(double realPart2) 複數相加
minus(ComplexNumber c) 複數相減
minus(double realPart2) 複數相減
ComplexMulti(ComplexNumber c) 複數相乘
ComplexMulti(double realPart2) 複數相乘
toString() 把當前複數對象的實部,虛部組合成a+bi的字串形式
產品代碼為:
測試代碼:
【實驗體會】
通過TDD模式編程,可以從結果出發,對產品代碼進行測試,並進行完善,能夠使自己的產品代碼更加嚴謹,更加完善。
這次實驗中,我不光學習到了很多開發過程中的原則,也體會到自己學習上的不足,需要我在今後的學習生活中多加練習。也學到了一些新的知識,如果發現bug或者新的功能添加,可以馬上通過運行單元測試來驗證之前完成的代碼是否正確。
實驗二 Java物件導向程式設計