大多數(shù)開發(fā)人員現(xiàn)在還在使用if else的過程結(jié)構(gòu),曾看過jdon的banq大哥寫的一篇文章,利用command,aop模式替代if else過程結(jié)構(gòu)。當(dāng)時還不太明白,這幾天看了《重構(gòu)》第一章的影片租賃案例,感觸頗深。下面我來談一談為什么要用state pattern替代if else,替代if else有什么好處,以及給出詳細(xì)代碼怎么替代if else。本文參考jdon的“你還在使用if else嗎?”及《重構(gòu)》第一章。
?
首先我們模仿影片租賃過程,顧客租憑影片,影片分為兒童片、普通片、新片。根據(jù)影片類型及租憑天數(shù)價格各不相同(優(yōu)惠程度不同),用戶累計積分不同。
?
OK
,現(xiàn)在我們使用
if else
表示。
package
?com.qujingbo.movie;


/**?*/
/**
?*?<p/>?Title:影片基類
?*?</p>
?*?<p/>?Description:
?*?</p>
?*?<p/>?Date:2006-10-14?15:47:55
?*?</p>
?*?
?*?
@author
?EOMS?曲靜波
?*?
@version
?1.0
?
*/
public
?
class
?Movie?
{

?????
//
?普通片標(biāo)識
?????
public
?
static
?
int
?REGULAR?
=
?
1
;
????
?????
//
?新片標(biāo)識
?????
public
?
static
?
int
?NEW_RELEASE?
=
?
2
;
????
?????
//
?兒童片標(biāo)識
?????
public
?
static
?
int
?CHILDREN?
=
?
3
;
????

?????
/**?*/
/**
?????*?獲取租賃影片總價
?????*
?????*?
@param
?movieCode
?????*?影片類型
?????*?
@param
?days
?????*?租憑天數(shù)
?????*?
@return
?租賃影片總價
?????*?
@throws
?MovieException
?????*?沒有影片類型拋出異常
?????
*/
?????
public
?
double
?getCharge(
int
?movieCode,?
int
?days)?
throws
?MovieException?
{
?????
double
?result?
=
?
0
;
?????
//
?普通片
?????
if
?(movieCode?
==
?Movie.REGULAR)
?????
//
?單價為2
?????
{
?????result?
=
?
2
;
?????
//
?如果租賃天數(shù)大于2則,則優(yōu)惠
?????
if
?(days?
>
?
2
)?
{
?????result?
+=
?(days?
-
?
2
)?
*
?
1.5
;
?????}
?????
//
?返回總價
?????
return
?result;
?????}
?????
//
?最新發(fā)布片
?????
else
?
if
?(movieCode?
==
?Movie.NEW_RELEASE)?
{
?????
//
?新片沒有優(yōu)惠,單價為3
?????
return
?days?
*
?
3
;
?????}
?????
//
?兒童片
?????
else
?
if
?(movieCode?
==
?Movie.CHILDREN)?
{
?????
//
?影片單價
?????result?
=
?
1.5
;
?????
//
?如果租賃時間大于3天則做價格優(yōu)惠
?????
if
?(days?
>
?
3
)?
{
?????result?
+=
?(days?
-
?
3
)?
*
?
1.5
;
?????}
?????
//
?返回租賃影片總價
?????
return
?result;
?????}
?
else
?????
throw
?
new
?MovieException(
"
影片不存在
"
);
?????}
????

?????
/**?*/
/**
?????*?獲取租賃影片積分
?????*
?????*?
@param
?movieCode
?????*?影片類型
?????*?
@param
?days
?????*?租憑天數(shù)
?????*?
@return
?租賃影片積分
?????*?
@throws
?MovieException
?????*?沒有影片類型拋出異常
?????
*/
?????
public
?
double
?getIntegral(
int
?movieCode,?
int
?days)?
throws
?MovieException

?????
{
?????
//
?普通片
?????
if
?(movieCode?
==
?Movie.REGULAR)
?????
return
?days?
*
?
2
;
?????
//
?最新發(fā)布片
?????
else
?
if
?(movieCode?
==
?Movie.NEW_RELEASE)
?????
return
?days?
*
?
3
;
?????
//
?兒童片
?????
else
?
if
?(movieCode?
==
?Movie.CHILDREN)
?????
return
?days?
*
?
1.5
;
????????????
else
????????????????
throw
?
new
?MovieException(
"
影片不存在
"
);
????
????????}
}
OK
,我們看一下,現(xiàn)在的
Movie
完全符合租賃需求,通過
getIntegral(int movieCode,int days)
和
getCharge(int movieCode,int days)
來獲得租賃積分及租賃價格。從開閉原則角度來看,如果要添加新的影片類型,我們必須修改
getIntegral(int movieCode,int days)
和
getCharge(int movieCode,int days)
這兩個方法。而若要改變租賃價格、積分的優(yōu)惠規(guī)則時,仍需要修改
getIntegral(int movieCode,int days)
和
getCharge(int movieCode,int days)
方法。現(xiàn)在看來,只有三種影片類型,維護(hù)還較方便。而當(dāng)影片類型較多時,例如
10
種,
100
種影片類型,這樣就是不可以想像的維護(hù)。
?
現(xiàn)在我們來看一下,使用
state pattern
來代替
if else
。先來個類圖。
?
?
首先我們建立一個
abstract class Price
做為影片類型的基類,基類中含有兩個
abstract
方法,獲取總價格
getCharge(int days),
獲取總積分
getIntegral(int days)
方法
,
繼承
abstract classPrice
的三個影片類型兒童片
class ChilerenPrice,
普通片
class RegularPrice,
最新片
class NewReleasePrice
。分別實現(xiàn)
getCharge(int days),getIntegral(int days)
方法,實現(xiàn)方法寫入計算價格的優(yōu)惠方案及積分的方案。當(dāng)需要修改方案時,我們只需在某個影片類的方法中對應(yīng)修改就可以。若新增一個影片分類時,我們只需新增一個實現(xiàn)類實現(xiàn)
abstract class Price
類就
OK
。
?
class Movie
代表影片,其關(guān)聯(lián)一個
Price
類,而
setPrice(String movieClass)
方法類似于一個工廠類,傳入
movieClass
為包名類名,用
java
反射機(jī)制實例化一個具體傳入
movieClass
的影片類型實現(xiàn)類,這樣我們通過這幾行代碼就可以獲得該影片類型的價格和積分。
Movie?regularMovie?
=
?
new
?Movie();
regularMovie.setPrice(Movie.REGULAR);
System.out.println(
"
普通影片租賃10天的價格
"
+
?regularMovie.getPrice().getCharge(
10
));
System.out.println(
"
普通影片租賃10天的積分
"
+
?regularMovie.getPrice().getIntegral(
10
));
下面我們給出詳細(xì)代碼
abstract class Price價格基類
package?com.qujingbo.movie;


/**?*//**
?*?<p/>?Title:
?*?</p>
?*?<p/>?Description:
?*?</p>
?*?<p/>?Date:2006-10-14?15:48:22
?*?</p>
?*?
?*?@author?EOMS?曲靜波
?*?@version?1.0
?*/

public?abstract?class?Price?
{


????/**?*//**
?????*?獲取租賃影片價格需實現(xiàn)該此方法
?????*?
?????*?@param?days
?????*????????????租賃天數(shù)
?????*?@return?返回影片價格
?????*/
????public?abstract?double?getCharge(int?days);


????/**?*//**
?????*?獲取租賃影片積分需實現(xiàn)此方法
?????*?
?????*?@param?days
?????*????????????租賃天數(shù)
?????*?@return?返回影片積分
?????*/
????public?abstract?double?getIntegral(int?days);

}

兒童片ChildrenPrice類,實現(xiàn)abstract class Price ,實現(xiàn)兒童片租賃總價getCharge(int days)及兒童片租賃積分getIntegral(int days)。
package?com.qujingbo.movie;


/**?*//**
?*?<p/>?Title:兒童片租賃積分、價格實現(xiàn)
?*?</p>
?*?<p/>?Description:
?*?</p>
?*?<p/>?Date:2006-10-14?15:49:04
?*?</p>
?*?
?*?@author?EOMS?曲靜波
?*?@version?1.0
?*/

public?class?ChildrenPrice?extends?Price?
{


????/**?*//**
?????*?兒童片返回租賃積分,兒童片積分規(guī)則為:?根據(jù)
?????*/

????public?double?getIntegral(int?days)?
{
????????//?返回租賃影片積分
????????return?days?*?1.5;
????}


????/**?*//**
?????*?兒童片返回租賃價格
?????*/

????public?double?getCharge(int?days)?
{
????????//?影片單價
????????double?result?=?1.5;
????????//?如果租賃時間大于3天則做價格優(yōu)惠

????????if?(days?>?3)?
{
????????????result?+=?(days?-?3)?*?1.5;
????????}
????????//?返回租賃影片總價
????????return?result;
????}

}

普通片RegularlPrice類,實現(xiàn)abstract class Price ,實現(xiàn)普通片租賃總價getCharge(int days)及普通片租賃積分getIntegral(int days)。
package?com.qujingbo.movie;


/**?*//**
?*?<p/>?Title:普通片租賃積分、價格實現(xiàn)
?*?</p>
?*?<p/>?Description:
?*?</p>
?*?<p/>?Date:2006-10-14?15:50:10
?*?</p>
?*?
?*?@author?EOMS?曲靜波
?*?@version?1.0
?*/

public?class?RegularlPrice?extends?Price?
{

????/**?*//**
?????*?普通片返回租賃積分,普通片積分規(guī)則
?????*/

????public?double?getIntegral(int?days)?
{
????????//?返回租賃影片積分
????????return?days?*?2;
????}


????/**?*//**
?????*?普通片返回租賃價格
?????*/

????public?double?getCharge(int?days)?
{
????????//?單價為2
????????double?result?=?2;
????????//?如果租賃天數(shù)大于2則,則優(yōu)惠

????????if?(days?>?2)?
{
????????????result?+=?(days?-?2)?*?1.5;
????????}
????????//?返回總價
????????return?result;
????}

}

最新發(fā)布片NewReleasePrice類,實現(xiàn)abstract class Price ,實現(xiàn)最新發(fā)布片租賃總價getCharge(int days)及最新發(fā)布片租賃積分getIntegral(int days)。
package?com.qujingbo.movie;


/**?*//**
?*?<p/>?Title:最新發(fā)布片租賃積分、價格實現(xiàn)
?*?</p>
?*?<p/>?Description:
?*?</p>
?*?<p/>?Date:2006-10-14?15:48:51
?*?</p>
?*?
?*?@author?EOMS?曲靜波
?*?@version?1.0
?*/

public?class?NewReleasePrice?extends?Price?
{

????/**?*//**
?????*?最新發(fā)布片返回租賃積分,最新發(fā)布片積分規(guī)則
?????*/

????public?double?getIntegral(int?days)?
{
????????//?返回租賃影片積分
????????return?days?*?3;
????}


????/**?*//**
?????*?最新發(fā)布片返回租賃價格
?????*/

????public?double?getCharge(int?days)?
{
????????//?新片沒有優(yōu)惠,單價為3
????????return?days?*?3;
????}

}

電影Movie類,setPrice(String movieClass)(工廠)方法,通過java反射機(jī)制實現(xiàn)movieClass(包名,類名)類。若沒有movieClass這個類,則拋出MovieException異常。
package?com.qujingbo.movie;


/**?*//**
?*?<p/>?Title:影片類
?*?</p>
?*?<p/>?Description:
?*?</p>
?*?<p/>?Date:2006-10-14?15:47:55
?*?</p>
?*?
?*?@author?EOMS?曲靜波
?*?@version?1.0
?*/

public?class?Movie?
{
????//?普通片標(biāo)識
????public?static?String?REGULAR?=?"com.qujingbo.movie.RegularlPrice";

????//?新片標(biāo)識
????public?static?String?NEW_RELEASE?=?"com.qujingbo.movie.NewReleasePrice";

????//?兒童片標(biāo)識
????public?static?String?CHILDREN?=?"com.qujingbo.movie.ChildrenPrice";

????private?Price?price;


????public?Price?getPrice()?
{
????????return?price;
????}


????/**?*//**
?????*?確定返回具體某個影片類型的實現(xiàn)類,有點像工廠
?????*?
?????*?@param?movieCode
?????*????????????影片類型
?????*?@throws?MovieException
?????*?????????????若無影片類型則拋異常。
?????*/

????public?void?setPrice(String?movieClass)?throws?MovieException?
{

????????try?
{
????????????Class?cls?=?Class.forName(movieClass);
????????????this.price?=?(Price)?cls.newInstance();

????????}?catch?(Exception?e)?
{
????????????throw?new?MovieException("影片不存在");
????????}
????}
}

給出MovieException源碼。
package?com.qujingbo.movie;


/**?*//**
?*?<p/>?Title:自定義異常
?*?</p>
?*?<p/>?Description:
?*?</p>
?*?<p/>?Date:2006-10-14?19:21:08
?*?</p>
?*?
?*?@author?EOMS?曲靜波
?*?@version?1.0
?*/

public?class?MovieException?extends?Exception?
{

????public?MovieException(String?msg)?
{
????????super(msg);
????}
}

下面模訪一個顧客租賃影片。
package?com.qujingbo.movie;


/**?*//**
?*?<p/>?Title:
?*?</p>
?*?<p/>?Description:
?*?</p>
?*?<p/>?Date:2006-10-14?19:26:23
?*?</p>
?*?
?*?@author?EOMS?曲靜波
?*?@version?1.0
?*/

public?class?Customer?
{

????/**?*//**
?????*?消費(測試程序)
?????*?
?????*?@throws?MovieException
?????*?????????????若沒有影片,拋出異常
?????*/

????public?void?consume()?throws?MovieException?
{
????????//?普通電影
????????Movie?regularMovie?=?new?Movie();
????????regularMovie.setPrice(Movie.REGULAR);
????????//?最新發(fā)布電影
????????Movie?newReleaseMovie?=?new?Movie();
????????newReleaseMovie.setPrice(Movie.NEW_RELEASE);
????????//?兒童電影
????????Movie?childrenMovie?=?new?Movie();
????????childrenMovie.setPrice(Movie.CHILDREN);

????????System.out.println("普通影片租賃10天的價格"
????????????????+?regularMovie.getPrice().getCharge(10));
????????System.out.println("最新影片租賃10天的價格"
????????????????+?newReleaseMovie.getPrice().getCharge(10));
????????System.out.println("兒童影片租賃10天的價格"
????????????????+?childrenMovie.getPrice().getCharge(10));
????????
????????System.out.println("普通影片租賃10天的積分"
????????????????+?regularMovie.getPrice().getIntegral(10));
????????System.out.println("最新影片租賃10天的積分"
????????????????+?newReleaseMovie.getPrice().getIntegral(10));
????????System.out.println("兒童影片租賃10天的積分"
????????????????+?childrenMovie.getPrice().getIntegral(10));
????????
????????
????}
}

寫一
junit
測試類運行
class Customer
的
consume()
方法。
package
?com.qujingbo.movie;

import
?junit.framework.TestCase;


/**?*/
/**
?*?<p/>?Title:junit測試類
?*?</p>
?*?<p/>?Description:
?*?</p>
?*?<p/>?Date:2006-10-14?19:32:57
?*?</p>
?*?
?*?
@author
?EOMS?曲靜波
?*?
@version
?1.0
?
*/
public
?
class
?CustomerTest?
extends
?TestCase?
{

????
private
?Customer?customer?
=
?
null
;


????
protected
?
void
?setUp()?
throws
?Exception?
{
????????
super
.setUp();
????????customer?
=
?
new
?Customer();
????}
????
protected
?
void
?tearDown()?
throws
?Exception?
{
????????
super
.tearDown();
????}
????
/**/
/*
?????*?Test?method?for?'com.qujingbo.movie.Customer.consume()'
?????
*/
????
public
?
void
?testConsume()?
{

????????
try
?
{
????????????customer.consume();

????????}
?
catch
?(MovieException?e)?
{
????????????System.out.println(
"
沒有該類影片
"
);
????????}
????}
}
OK
。結(jié)果為:
普通影片租賃
10
天的價格
14.0
最新影片租賃
10
天的價格
30.0
兒童影片租賃
10
天的價格
12.0
普通影片租賃
10
天的積分
20.0
最新影片租賃
10
天的積分
30.0
兒童影片租賃
10
天的積分
15.0
?
最后我要說,我們用
OO
表示的租賃過程并不完整,因為顧客不一定只租賃一部影片,而要租賃多部影片,這樣我們?nèi)鄙僖粋€
Rental
(租賃類)。而只是為說明
state pattern
替代
if else
,所以我們沒有添加
Rental
(租賃類),若需要參考,請查閱《重構(gòu)》第一章。 點擊下載源碼.
?
這是我第一次寫技術(shù)文章,如果有適當(dāng)?shù)牡胤剑埜魑慌笥烟岢龈髯砸娊狻?/span>
posted on 2006-10-16 13:46
xzc 閱讀(3140)
評論(0) 編輯 收藏 所屬分類:
Design