使用狀態模式(state pattern)替代if else
http://www.blogjava.net/xzclog/archive/2006/10/16/75399.html
大多數開發人員現在還在使用if else的過程結構,曾看過jdon的banq大哥寫的一篇文章,利用command,aop模式替代if else過程結構。當時還不太明白,這幾天看了《重構》第一章的影片租賃案例,感觸頗深。下面我來談一談為什麼要用state pattern替代if else,替代if else有什麼好處,以及給出詳細程式碼怎麼替代if else。本文參考jdon的“你還在使用if else嗎?”及《重構》第一章。
首先我們模仿影片租賃過程,顧客租憑影片,影片分為兒童片、普通片、新片。根據影片型別及租憑天數價格各不相同(優惠程度不同),使用者累計積分不同。
OK ,現在我們使用 if else 表示。
/**
* <p/> Title:影片基類
* </p>
* <p/> Description:
* </p>
* <p/> Date:2006-10-14 15:47:55
* </p>
*
* @author EOMS 曲靜波
* @version 1.0
*/
public class Movie {
// 普通片標識
public static int REGULAR = 1 ;
public static int NEW_RELEASE = 2 ;
// 兒童片標識
public static int CHILDREN = 3 ;
/**
* 獲取租賃影片總價
*
* @param movieCode
* 影片型別
* @param days
* 租憑天數
* @return 租賃影片總價
* @throws MovieException
* 沒有影片型別丟擲異常
*/
public
double result = 0 ;
// 普通片
if (movieCode == Movie.REGULAR)
// 單價為2
{
result = 2 ;
// 如果租賃天數大於2則,則優惠
if (days > 2 ) {
result += (days - 2 ) * 1.5 ;
}
// 返回總價
return result;
}
// 最新發布片
else if (movieCode == Movie.NEW_RELEASE) {
// 新片沒有優惠,單價為3
return days * 3 ;
}
// 兒童片
else if (movieCode == Movie.CHILDREN) {
// 影片單價
result = 1.5 ;
// 如果租賃時間大於3天則做價格優惠
if (days > 3 ) {
result += (days - 3 ) * 1.5 ;
}
// 返回租賃影片總價
return result;
} else
throw new MovieException( " 影片不存在 " );
}
/**
* 獲取租賃影片積分
*
* @param movieCode
* 影片型別
* @param days
* 租憑天數
* @return 租賃影片積分
* @throws MovieException
* 沒有影片型別丟擲異常
*/
public double getIntegral( int movieCode, int days) throws MovieException
{
// 普通片
if (movieCode == Movie.REGULAR)
return days * 2 ;
// 最新發布片
else if (movieCode == Movie.NEW_RELEASE)
return days * 3 ;
// 兒童片
else if (movieCode == Movie.CHILDREN)
return days * 1.5 ;
else
throw new MovieException( " 影片不存在 " );
}
}
OK ,我們看一下,現在的 Movie 完全符合租賃需求,通過 getIntegral(int movieCode,int days) 和 getCharge(int movieCode,int days) 來獲得租賃積分及租賃價格。從開閉原則角度來看,如果要新增新的影片型別,我們必須修改 getIntegral(int movieCode,int days) 和 getCharge(int movieCode,int days) 這兩個方法。而若要改變租賃價格、積分的優惠規則時,仍需要修改 getIntegral(int movieCode,int days) 和 getCharge(int movieCode,int days) 方法。現在看來,只有三種影片型別,維護還較方便。而當影片型別較多時,例如 10 種, 100 種影片型別,這樣就是不可以想像的維護。
現在我們來看一下,使用 state pattern 來代替 if else 。先來個類圖。
首先我們建立一個 abstract class Price 做為影片型別的基類,基類中含有兩個 abstract 方法,獲取總價格 getCharge(int days), 獲取總積分 getIntegral(int days) 方法 , 繼承abstract classPrice 的三個影片型別兒童片 class ChilerenPrice, 普通片 class RegularPrice, 最新片 class NewReleasePrice 。分別實現 getCharge(int days),getIntegral(int days) 方法,實現方法寫入計算價格的優惠方案及積分的方案。當需要修改方案時,我們只需在某個影片類的方法中對應修改就可以。若新增一個影片分類時,我們只需新增一個實現類實現 abstract class Price 類就 OK 。
class Movie 代表影片,其關聯一個 Price 類,而 setPrice(String movieClass) 方法類似於一個工廠類,傳入 movieClass 為包名類名,用 java 反射機制例項化一個具體傳入movieClass 的影片型別實現類,這樣我們通過這幾行程式碼就可以獲得該影片型別的價格和積分。
regularMovie.setPrice(Movie.REGULAR);
System.out.println( " 普通影片租賃10天的價格 " + regularMovie.getPrice().getCharge( 10 ));
System.out.println( " 普通影片租賃10天的積分 " + regularMovie.getPrice().getIntegral( 10 ));
下面我們給出詳細程式碼
abstract class Price價格基類
/**
* <p/> Title:
* </p>
* <p/> Description:
* </p>
* <p/> Date:2006-10-14 15:48:22
* </p>
*
* @author EOMS 曲靜波
* @version 1.0
*/
publicabstractclass Price {
/**
* 獲取租賃影片價格需實現該此方法
*
* @param days
* 租賃天數
* @return 返回影片價格
*/
publicabstractdouble getCharge(int days);
/**
* 獲取租賃影片積分需實現此方法
*
* @param days
* 租賃天數
* @return 返回影片積分
*/
publicabstractdouble getIntegral(int days);
}
兒童片ChildrenPrice類,實現abstract class Price ,實現兒童片租賃總價getCharge(int days)及兒童片租賃積分getIntegral(int days)。
package com.qujingbo.movie;
/**
* <p/> Title:兒童片租賃積分、價格實現
* </p>
* <p/> Description:
* </p>
* <p/> Date:2006-10-14 15:49:04
* </p>
*
* @author EOMS 曲靜波
* @version 1.0
*/
publicclass ChildrenPrice extends Price {
/**
* 兒童片返回租賃積分,兒童片積分規則為: 根據
*/
publicdouble getIntegral(int days) {
// 返回租賃影片積分
return days *1.5;
}
/**
* 兒童片返回租賃價格
*/
publicdouble getCharge(int days) {
// 影片單價
double result =1.5;
// 如果租賃時間大於3天則做價格優惠
if (days >3) {
result += (days -3) *1.5;
}
// 返回租賃影片總價
return result;
}
}
普通片RegularlPrice類,實現abstract class Price ,實現普通片租賃總價getCharge(int days)及普通片租賃積分getIntegral(int
days)。
/**
* <p/> Title:普通片租賃積分、價格實現
* </p>
* <p/> Description:
* </p>
* <p/> Date:2006-10-14 15:50:10
* </p>
*
* @author EOMS 曲靜波
* @version 1.0
*/
publicclass RegularlPrice extends Price {
/**
* 普通片返回租賃積分,普通片積分規則
*/
publicdouble getIntegral(int days) {
// 返回租賃影片積分
return days *2;
}
/**
* 普通片返回租賃價格
*/
publicdouble getCharge(int days) {
// 單價為2
double result =2;
// 如果租賃天數大於2則,則優惠
if (days >2) {
result += (days -2) *1.5;
}
// 返回總價
return result;
}
}
最新發布片NewReleasePrice類,實現abstract class Price ,實現最新發布片租賃總價getCharge(int days)及最新發布片租賃積分getIntegral(int
days)。
/**
* <p/> Title:最新發布片租賃積分、價格實現