面向對象軟件開發的敏捷過程(四) 完
繼續檢查 rental 類中函數 getCharge() 的語句 switch (getMovie().getPriceCode()) ,它提示我們應該將計算 charge 的職責交給 movie 類來完成。這是租借天數作為參數傳給 movie 類的相關函數進行計算。我們在 Movie 類添加函數如下:
double getCharge(int dayRented){
????????????? double result=0.0;
????????????? switch(this.getPriceCode()){
????????????? case CHILDRENS:
???????????????????? result+=1.5;
???????????????????? if(dayRented>3){
??????????????????????????? result+=(dayRented-3)*1.5;
???????????????????? }
???????????????????? break;
????????????? case REGULAR:
???????????????????? result+=2;
???????????????????? if(dayRented>2){
??????????????????????????? result+=(dayRented-2)*1.5;
???????????????????? }
???????????????????? break;
????????????? case NEW_RELEASE:
???????????????????? result=dayRented*3;
???????????????????? break;
????????????? }
????????????? return result;
?????? }
原來的 rental 類中 getcharge ()函數中原來的函數體全部注釋掉,只添加一行對 movie 類中新添函數的委托調用。 return movie.getCharge(this.dayRented);
繼續運行測試,通過了,我們可以放心大膽的刪除注釋掉的函數體。
在 movie 類中添加函數
public int getFrequentCount(int dayRented) {
????????????? if (getPriceCode() == Movie.NEW_RELEASE && dayRented > 1)
???????????????????? return 2;
????????????? else
???????????????????? return 1;
?????? }
在原來的 rental 類中 getFrequentCount ()函數中原有的函數體全部注釋掉,添加對 movie 類中新添函數的委托調用。
return movie.getFrequentCount(this.getDayRented());
繼續運行測試,防止我們在更改時破壞了任何東西。
最后,由于 movie 的類型是相對比較容易發生變化的,我們必須把具體類型的變化封裝出來。這也是封裝變化的要求。下面我們可以有兩種解決方案:
解決方案一:
將
movie
的多個類型的計算可以通過繼承共同的基類型通過多態完成具體類型的差異運算。
這種解決方法無法解決影片在運行期解決所屬類型的需要。如隨著時間推移,新片超過一定時間后,會按照普通片或者兒童片進行租費收取。完成此功能我們將借助狀態模式的解決方案。
解決方案二:
這里影片的計費策略委托給定價類的具體子類來實現。為了簡單起見,我們不引入獨立的工廠,把簡單工廠的職責交給定價類進行實現。
在類 movie 的構造函數中,我們將類別代碼使用 set 函數賦值,以下是更改后的構造函數:
public Movie(String title, int priceCode) {
?????????????
????????????? this.title = title;
????????????? setPriceCode(priceCode);
?????? }
加入新的類體系如下:
abstract public class Price {
?????? abstract int getPriceCode();
}
public class NewReleasePrice extends Price {
?????? @Override
?????? int getPriceCode() {
????????????? // TODO Auto-generated method stub
????????????? return Movie.NEW_RELEASE;
?????? }
}
public class RegularPrice extends Price {
?????? @Override
?????? int getPriceCode() {
????????????? // TODO Auto-generated method stub
????????????? return Movie.REGULAR;
?????? }
}
public class ChildrenPrice extends Price {
?????? @Override
?????? int getPriceCode() {
????????????? // TODO Auto-generated method stub
????????????? return Movie.CHILDRENS;
?????? }
}
在 movie 類中調用新的類體系:
將 private int priceCode; 改為 private Price price; 使用新的對象委托。
將原來對應的 set/get 函數修改為:
public int getPriceCode() {
????????????? return price.getPriceCode();
?????? }
?????? public void setPriceCode(int priceCode) {
????????????? switch(priceCode){
????????????? case REGULAR:
???????????????????? price=new RegularPrice();
???????????????????? break;
????????????? case CHILDRENS:
???????????????????? price=new ChildrenPrice();
???????????????????? break;
????????????? case NEW_RELEASE:
???????????????????? price=new NewReleasePrice();
???????????????????? break;
????????????? }
?????? }
然后我們把 movie 類中的 getcharge ()函數移動到 price 類中
在 price 類中添加函數:
double getCharge(int dayRented){
????????????? double result=0.0;
????????????? switch(getPriceCode()){
????????????? case Movie.CHILDRENS:
???????????????????? result+=1.5;
???????????????????? if(dayRented>3){
??????????????????????????? result+=(dayRented-3)*1.5;
???????????????????? }
???????????????????? break;
????????????? case Movie.REGULAR:
???????????????????? result+=2;
???????????????????? if(dayRented>2){
??????????????????????????? result+=(dayRented-2)*1.5;
???????????????????? }
???????????????????? break;
????????????? case Movie.NEW_RELEASE:
???????????????????? result=dayRented*3;
???????????????????? break;
????????????? }
????????????? return result;
?????? }
把 movie 類中 getcharge ()函數的函數體改為委托 price 的相應操作
return price.getCharge(dayRented);
接下來,把相應的類型分支語句用多態進行替換
在 RegularPrice 中
@Override
?????? double getCharge(int dayRented) {
????????????? // TODO Auto-generated method stub
????????????? double result=2;
????????????? if (dayRented>2){
???????????????????? result+=(dayRented-2)*1.5;
????????????? }
????????????? return result;
?????? }
在類 ChildrenPrice 中:
@Override
?????? double getCharge(int dayRented) {
????????????? // TODO Auto-generated method stub
????????????? double result=1.5;
????????????? if (dayRented>3){
???????????????????? result+=(dayRented-3)*1.5;
????????????? }
????????????? return result;
?????? }
在類 NewReleasePrice 中
@Override
?????? double getCharge(int dayRented) {
????????????? // TODO Auto-generated method stub
????????????? return 3*dayRented;
?????? }
把基類中的對應函數聲明為抽象,使編譯器必須調用子類型的對應重載函數實現。
abstract double getCharge(int dayRented);
測試通過。
接下來把類 movie 中函數 getFrequentCount 移動到類 price 中
public int getFrequentCount(int dayRented) {
??????
????????????? if (getPriceCode() == Movie.NEW_RELEASE && dayRented > 1)
???????????????????? return 2;
????????????? else
???????????????????? return 1;
?????? }
原有的函數體中更改為委托調用。
return price.getFrequentCount(dayRented);
然后將 price 中 getFrequentCount 的函數體改寫為
public int getFrequentCount(int dayRented) {
??????????????????????????? return 1;?
?????? }
在類 NewReleasePrice 中重載實現
@Override
?????? public int getFrequentCount(int dayRented) {
????????????? // TODO Auto-generated method stub
????????????? if (dayRented>1)
???????????????????? return 2;
????????????? else
????????????? return super.getFrequentCount(dayRented);
?????? }
引入狀態模式以后,我們如果想修改定價方法或是添加新的類型都會變得很容易,例如每種影片的每日租金和最短附加期限等發生變化時只需要更改對應的子類,其余的模塊和類都不需改變。
功能擴展:
使用重構功能更改業務邏輯后,我們可以使用對象 / 關系數據庫映射框架靈活的把對象存儲到數據庫中。這方面可以使用 HIBERNATE 和 IBATIS 等關系數據庫持久化框架。
限于篇幅,具體實現就不展示了。
結論:
應用敏捷軟件開發過程和靈活的面向對象設計思想,軟件的功能可以穩定快速的擴充。自動化可回歸的測試套件,保證了軟件修改前后功能的一致性。運用有效、小步的重構結合測試,設計可以逐步優化,并且有目的的應用設計模式,為后期維護提供了有效的辦法。
參考資料:
《設計模式:可復用面向對象軟件的基礎 》 GoF
《重構 - 改善現有代碼的設計》 martin fowler
《敏捷軟件開發:原則模式和實踐》 robert martin
《 java 和模式》閻宏
《軟件復用 - 結構過程和組織》 韓柯
《設計之道》 張逸
《 oo 開發進階系列》 方春旭 胡協剛
《 Head First Design Patterns 》 eric freeman etc
《 Thinking In Java 》 Bruce.Eckel
非程序員網站 http://www.umlchina.com/
軟件測試網 http://www.51testing.com/