為了能在Java應用程序中正確的使用狀態模式和策略模式,開發人員需要清楚地知道這兩種模式之間的區別。盡管狀態模式和策略模式的結構非常相似,它們又同樣遵循開閉原則,都代表著SOLID設計原則的'O',但它們的意圖是完全不同的。Java中的策略模式是對一組相關的算法進行封裝,給調用方提供了運行時的靈活性。調用方可以在運行時選擇不同的算法,而不用修改使用策略的那個Context類。使用策略模式的經典例子包括實現加密算法,壓縮算法,以及排序算法。另一方面,狀態模式使用一個對象可以在不同的狀態下表現出不同的行為。真實世界里的對象也是有狀態的,并且它們會隨著狀態的不同而有不同的表現,比方說自動售貨機,它只會在hasCoin狀態下才能出售物品,如果你不塞硬幣進去它是不會售貨的。現在你可以很清楚地看到策略模式和狀態模式的區別了,它們的目的是不一樣的。狀態模式可以幫助對象來管理它的狀態,而策略模式使得客戶端可以選擇不同的行為。還有一個不太容易看到的區別是,誰去驅動行為的改變。在策略模式中,是客戶端驅動的,它給上下文信息提供了不同的策略,而在狀態模式中,狀態的遷移是由Context或者State對象自己來管理的。同樣的,如果你在State對象里面進行狀態的修改,它必須持有Context的引用,也就是說對自動售貨機而言,它可以調用setState方法來修改當前Context的狀態。另一方面,策略對象不會持有Context的引用 ,它的客戶端將選中的策略傳遞給Context。策略模式和狀態模式是最容易碰見的關于Java設計模式的面試題,在這篇關于Java設計模式的文章里,我們將會對這點進行詳細的介紹。我們會探索這兩種模式的相同點與不同點,這有助于提高你對這兩種模式的理解。
狀態模式和策略模式的相似點:
如果你看下策略模式和狀態模式的UML圖,它們看起來非常相似。在狀態模式中,使用State對象來改變行為的的對象叫Context對象,類似的在策略模式中,使用Strategy對象來改變行為的對象也是Context對象。記住,客戶端是和Context對象交互的。在狀態模式中,Context代理了狀態對象的方法調用,Context中的當前對象就是具體的狀態對象,而在策略模式中,Context操作的也是策略對象,這個對象要么作為參數傳入進來,要么是在創建Context對象的時候就已經提供了。
我們再來看一下這兩種核心的Java設計模式的一些相似點:
狀態模式和策略模式都很容易新增新的狀態或者策略,而不會影響到使用它們的Context對象
兩種模式都遵循開閉的設計原則,也就是說你的設計對擴展開放而對修改關閉。在這兩個模式里,Context對修改是封閉的,新增狀態或者策略,你不需要修改其它狀態的Context對象,或者只需要很小的改動
正如狀態模式中Context對象會有一個初始狀態一樣,策略模式中的Context通常也有一個默認的策略。
狀態模式以不同的狀態對象的方式來封裝不同的行為,而策略模式以不同的策略對象來封裝不同的行為。
這兩種模式都依賴具體的子類來實現具體的行為。每一個具體的策略都擴展自一個抽象的策略類,每個狀態也都是用來表示狀態的接口或者抽象類的子類。
狀態模式實例
public class WindowState { private String stateValue; public WindowState(String stateValue) { this.stateValue = stateValue; } public String getStateValue() { return stateValue; } public void setStateValue(String stateValue) { this.stateValue = stateValue; } public void handle() { /* * 根據不同狀態做不同操作, 再切換狀態 */ if ("窗口".equals(stateValue)) { switchWindow(); this.stateValue = "全屏"; } else if ("全屏".equals(stateValue)) { switchFullscreen(); this.stateValue = "窗口"; } } private void switchWindow() { System.out.println("切換為窗口狀態"); } private void switchFullscreen() { System.out.println("切換為全屏狀態"); } } /** * 狀態的使用 */ public class WindowContext { private WindowState state; public WindowContext(WindowState state) { this.state = state; } public WindowState getState() { return state; } public void setState(WindowState state) { this.state = state; } public void switchState() { this.state.handle(); } } /* * 狀態(State)模式 行為型模式 * 既改變對象的狀態,又改變對象的行為 * 根據狀態,改變行為 */ public class Test { public static void main(String[] args) { /* * 本例的 狀態值只有兩個,由狀態類自身控制 * 也可以把狀態值的控制,交由客戶端來設置 */ WindowContext context = new WindowContext(new WindowState("窗口")); context.switchState(); context.switchState(); context.switchState(); context.switchState(); } } 打印
切換為窗口狀態 切換為全屏狀態 切換為窗口狀態 切換為全屏狀態
策略模式實例
/** * 商品促銷 * 本類為:收取現金的類 */ public interface ICashSuper { double acceptCash(double money); } /** * 正常收取現金 * @author stone * */ public class CashNormal implements ICashSuper { @Override public double acceptCash(double money) { return money; } } /** * 打折收取現金 * @author stone * */ public class CashRebate implements ICashSuper { private double rebate; //折扣 public CashRebate (double rebate) { this.rebate = rebate; } @Override public double acceptCash(double money) { return new BigDecimal(money * rebate / 10).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); } } /** * 讓利返現 收取現金 * @author stone * */ public class CashReturn implements ICashSuper { private double moneyCondition; //返現底限金額 private double returnMoney; //返還金額 public CashReturn(double moneyCondition, double returnMoney) { this.moneyCondition = moneyCondition; this.returnMoney = returnMoney; } @Override public double acceptCash(double money) {//多重返利 if (money >= moneyCondition) { return money - Math.floor(money / moneyCondition) * returnMoney; } else { return money; } } } /** * 根據傳遞的的策略類,執行相應的行為 */ public class CashContext { private ICashSuper casher; public CashContext() { } public CashContext(ICashSuper casher) { this.casher = casher; } public void setCasher(ICashSuper casher) { this.casher = casher; } //根據具體的策略對象,調用它的算法行為 public double acceptCash(double money) { return this.casher.acceptCash(money); } } public class Test { public static void main(String[] args) { double money = 998; //原價 CashContext cashContext = new CashContext(new CashNormal()); System.out.println("原價:" + cashContext.acceptCash(money)); //通常 策略 cashContext.setCasher(new CashRebate(8.5)); System.out.println("打85折:" + cashContext.acceptCash(money)); //折扣 策略 85折 cashContext.setCasher(new CashReturn(300, 50)); System.out.println("滿300 返50:" + cashContext.acceptCash(money)); //返現 策略 滿300 返50 } } 打印
原價:998.0 打85折:848.3 滿300 返50:848.0
策略模式和狀態模式的區別
我們已經了解到這兩個模式在結構上非常相似,但它們仍有不同的地方。下面來看下它們之間一些關鍵的不同點。
這就是關于Java中策略模式和狀態模式的所有區別。正如我所說的,它們在UML圖中看起來非常類似,兩者都遵循了開閉原則,并且封裝了行為。策略模式是用來封裝算法或者策略的,它會在運行時作為參數或者組合對象來提供給Context對象,而狀態模式則是用來管理狀態遷移 的。
新聞熱點
疑難解答