前言
大多數開發人員現在還在使用if else的過程結構,曾看過jdon的banq大哥寫的一篇文章,利用command,aop模式替代if else過程結構。當時還不太明白,這幾天看了《重構》第一章的影片租賃案例,感觸頗深。下面我來談一談為什麼要用state pattern替代if else,替代if else有什麼好處,以及給出詳細程式碼怎麼替代if else。
狀態模式簡介意圖:允許物件在內部狀態發生改變時改變它的行為,物件看起來好像修改了它的類。
主要解決:物件的行為依賴於它的狀態(屬性),並且可以根據它的狀態改變而改變它的相關行為。
何時使用:程式碼中包含大量與物件狀態有關的條件語句。
如何解決:將各種具體的狀態類抽象出來。
需求我們模仿商場根據不同日期來返回不同的價格,日期是不固定的,隨時可能要增加,優惠力度也不是固定的。
傳統if-else寫法/** * 根據不同的日期狀態值獲取不同的折後價格 */public class IfElseCode { private static int NORMAL = 1;//平時不打折 private static int CHRISTMAS = 2;//聖誕節打8折 private static int NEW_YEAR = 3;//新年打7折 /** * 獲取價格 * @param dateCode 日期編碼 * @param fullMoney 原價 * @return 折後的價格 */ public BigDecimal getCharge(int dateCode, BigDecimal fullMoney) throws BusinessException{ if(NORMAL == dateCode){ return fullMoney.multiply(BigDecimal.valueOf(1)); }else if(CHRISTMAS == dateCode){ return fullMoney.multiply(BigDecimal.valueOf(0.8)); }else if(NEW_YEAR == dateCode){ return fullMoney.multiply(BigDecimal.valueOf(0.7)); }else { throw new BusinessException("輸入的日期編碼有誤!"); } }
缺點:如果有新增日期打折型別,就需要頻繁的修改此處的程式碼,不符合開閉原則!
三、使用狀態模式3.1、定義狀態介面
/** * 狀態頂級介面 */public interface CashState { void doAction(Context context);//初始化方法 BigDecimal getCharge(BigDecimal fullPrice);//獲取最終價格方法}
3.2、各個狀態實現類
/** * 日常不打折的狀態 */public class NormalState implements CashState { @Override public void doAction(Context context) { context.setState(this); } @Override public BigDecimal getCharge(BigDecimal fullPrice) { return fullPrice.multiply(BigDecimal.valueOf(1)); } public String toString(){ return "正常不打折"; }}
/** * 聖誕節的狀態 */public class ChristmasState implements CashState { @Override public void doAction(Context context) { context.setState(this); } @Override public BigDecimal getCharge(BigDecimal fullPrice) { return fullPrice.multiply(BigDecimal.valueOf(0.8)); } public String toString(){ return "聖誕節打8折"; }}
/** * 新年的狀態 */public class NewYearState implements CashState { @Override public void doAction(Context context) { context.setState(this); } @Override public BigDecimal getCharge(BigDecimal fullPrice) { return fullPrice.multiply(BigDecimal.valueOf(0.7)); } public String toString(){ return "新年打7折"; }}
3.3、定義上下文
/** * 上下文 */public class Context { private CashState state; public Context(){ state = null; } public void setState(CashState state){ this.state = state; } public CashState getState(){ return state; }}
3.4、呼叫示例
public class StatePatternMain { public static void main(String[] args) { BigDecimal fullPrice = BigDecimal.valueOf(10); Context context = new Context(); /**************日常不打折狀態************/ CashState normalState = new NormalState(); normalState.doAction(context); System.out.println(context.getState().toString() +" ,最終價格="+ context.getState().getCharge(fullPrice)); /**************聖誕節的狀態************/ CashState christmasState = new ChristmasState(); christmasState.doAction(context); System.out.println(context.getState().toString() +" ,最終價格="+ context.getState().getCharge(fullPrice)); /**************新年的狀態************/ CashState newYearState = new NewYearState(); newYearState.doAction(context); System.out.println(context.getState().toString() +" ,最終價格="+ context.getState().getCharge(fullPrice)); }}
3.5、結果展示
總結應用例項:
1、打籃球的時候運動員可以有正常狀態、不正常狀態和超常狀態。
2、曾侯乙編鐘中,'鍾是抽象介面','鍾A'等是具體狀態,'曾侯乙編鐘'是具體環境(Context)。優點:
1、封裝了轉換規則。2、列舉可能的狀態,在列舉狀態之前需要確定狀態種類。
3、將所有與某個狀態有關的行為放到一個類中,並且可以方便地增加新的狀態,只需要改變物件狀態即可改變物件的行為。
4、允許狀態轉換邏輯與狀態物件合成一體,而不是某一個巨大的條件語句塊。
5、可以讓多個環境物件共享一個狀態物件,從而減少系統中物件的個數。
缺點:
1、狀態模式的使用必然會增加系統類和物件的個數。
2、狀態模式的結構與實現都較為複雜,如果使用不當將導致程式結構和程式碼的混亂。
3、狀態模式對"開閉原則"的支援並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的原始碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需修改對應類的原始碼。
使用場景:
1、行為隨狀態改變而改變的場景。
2、條件、分支語句的代替者。