|
24.1 APP 抽獎活動問題
請編寫程序完成 APP 抽獎活動 具體要求如下: 1) 假如每參加一次這個活動要扣除用戶 50 積分,中獎概率是 10% 2) 獎品數(shù)量固定,抽完就不能抽獎 3) 活動有四個狀態(tài): 可以抽獎、不能抽獎、發(fā)放獎品和獎品領完 4) 活動的四個狀態(tài)轉(zhuǎn)換關系圖(下圖)
24.1 狀態(tài)模式基本介紹基本介紹 1) 狀態(tài)模式(State Pattern):它主要用來解決對象在多種狀態(tài)轉(zhuǎn)換時,需要對外輸出不同的行為的問題。狀態(tài)和行為是一一對應的,狀態(tài)之間可以相互轉(zhuǎn)換 2) 當一個對象的內(nèi)在狀態(tài)改變時,允許改變其行為,這個對象看起來像是改變了其類 24.2 狀態(tài)模式的原理類圖
1) Context 類為環(huán)境角色, 用于維護 State 實例,這個實例定義當前狀態(tài) 2) State 是抽象狀態(tài)角色,定義一個接口封裝與 Context 的一個特點接口相關行為 3) ConcreteState 具體的狀態(tài)角色,每個子類實現(xiàn)一個與 Context 的一個狀態(tài)相關行為 24.1 狀態(tài)模式解決 APP 抽獎問1) 應用實例要求 完成 APP 抽獎活動項目,使用狀態(tài)模式. 2) 思路分析和圖解(類圖)
3)代碼實現(xiàn)
package com.lin.state; /** * 狀態(tài)抽象類 * * @author Administrator * */ public abstract class State { // 扣除積分 - 50 public abstract void deductMoney(); // 是否抽中獎品 public abstract boolean raffle(); // 發(fā)放獎品 public abstract void dispensePrize(); }
package com.lin.state; /** * 抽獎活動 // * * @author Administrator * */ public class RaffleActivity { // state 表示活動當前的狀態(tài),是變化 State state = null; // 獎品數(shù)量 int count = 0; // 四個屬性,表示四種狀態(tài) State noRafflleState = new NoRaffleState(this); State canRaffleState = new CanRaffleState(this); State dispenseState = new DispenseState(this); State dispensOutState = new DispenseOutState(this); // 構造器 // 1. 初始化當前的狀態(tài)為 noRafflleState(即不能抽獎的狀態(tài)) // 2. 初始化獎品的數(shù)量 public RaffleActivity(int count) { this.state = getNoRafflleState(); this.count = count; } // 扣分, 調(diào)用當前狀態(tài)的 deductMoney public void debuctMoney() { state.deductMoney(); } // 抽獎 public void raffle() { // 如果當前的狀態(tài)是抽獎成功 if (state.raffle()) { // 領取獎品state.dispensePrize(); } } public State getState() { return state; } public void setState(State state) { this.state = state; } // 這里請大家注意,每領取一次獎品,count-- public int getCount() { int curCount = count; count--; return curCount; } public void setCount(int count) { this.count = count; } public State getNoRafflleState() { return noRafflleState; } public void setNoRafflleState(State noRafflleState) { this.noRafflleState = noRafflleState; } public State getCanRaffleState() { return canRaffleState; } public void setCanRaffleState(State canRaffleState) { this.canRaffleState = canRaffleState; } public State getDispenseState() { return dispenseState; } public void setDispenseState(State dispenseState) { this.dispenseState = dispenseState; } public State getDispensOutState() { return dispensOutState; } public void setDispensOutState(State dispensOutState) { this.dispensOutState = dispensOutState; } }
package com.lin.state; /** *不能抽獎狀態(tài) *@author Administrator * */ public class NoRaffleState extends State { // 初始化時傳入活動引用,扣除積分后改變其狀態(tài) RaffleActivity activity; public NoRaffleState(RaffleActivity activity) { this.activity = activity; } // 當前狀態(tài)可以扣積分 , 扣除后,將狀態(tài)設置成可以抽獎狀態(tài) @Override public void deductMoney() { System.out.println("扣除 50 積分成功,您可以抽獎了"); activity.setState(activity.getCanRaffleState()); } // 當前狀態(tài)不能抽獎 @Override public boolean raffle() { System.out.println("扣了積分才能抽獎喔!"); return false; } // 當前狀態(tài)不能發(fā)獎品 @Override public void dispensePrize() { System.out.println("不能發(fā)放獎品"); } }
package com.lin.state; /** * 發(fā)放獎品的狀態(tài) * * @author Administrator * */ public class DispenseState extends State { // 初始化時傳入活動引用,發(fā)放獎品后改變其狀態(tài) RaffleActivity activity; public DispenseState(RaffleActivity activity) { this.activity = activity; } // @Override public void deductMoney() { System.out.println("不能扣除積分"); } @Override public boolean raffle() { System.out.println("不能抽獎"); return false; } //發(fā)放獎品 @Override public void dispensePrize() { if (activity.getCount() > 0) { System.out.println("恭喜中獎了"); // 改變狀態(tài)為不能抽獎 activity.setState(activity.getNoRafflleState()); } else { System.out.println("很遺憾,獎品發(fā)送完了"); // 改變狀態(tài)為獎品發(fā)送完畢, 后面我們就不可以抽獎 activity.setState(activity.getDispensOutState()); //System.out.println("抽獎活動結束"); //System.exit(0); } } }
package com.lin.state; /** * 獎品發(fā)放完畢狀態(tài) 說明,當我們 activity 改變成 DispenseOutState, 抽獎活動結束 * * @author Administrator * */ public class DispenseOutState extends State { // 初始化時傳入活動引用 RaffleActivity activity; public DispenseOutState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("獎品發(fā)送完了,請下次再參加"); } @Override public boolean raffle() { System.out.println("獎品發(fā)送完了,請下次再參加"); return false; } @Override public void dispensePrize() { System.out.println("獎品發(fā)送完了,請下次再參加"); } }
package com.lin.state; import java.util.Random; /** * 可以抽獎的狀態(tài) * * @author Administrator * */ public class CanRaffleState extends State { RaffleActivity activity; public CanRaffleState(RaffleActivity activity) { this.activity = activity; } // 已經(jīng)扣除了積分,不能再扣 @Override public void deductMoney() { System.out.println("已經(jīng)扣取過了積分"); } // 可以抽獎, 抽完獎后,根據(jù)實際情況,改成新的狀態(tài) @Override public boolean raffle() { System.out.println("正在抽獎,請稍等!"); Random r = new Random(); int num = r.nextInt(10); // 10%中獎機會 if (num == 0) { // 改 變 活 動 狀 態(tài) 為 發(fā) 放 獎 品 context activity.setState(activity.getDispenseState()); return true; } else { System.out.println("很遺憾沒有抽中獎品!"); // 改變狀態(tài)為不能抽獎 activity.setState(activity.getNoRafflleState()); return false; } } // 不能發(fā)放獎品 @Override public void dispensePrize() { System.out.println("沒中獎,不能發(fā)放獎品"); } }
package com.lin.state; /** * 狀態(tài)模式測試類 * * @author Administrator * */ public class Client { public static void main(String[] args) { // 創(chuàng)建活動對象,獎品有 1 個獎品 RaffleActivity activity = new RaffleActivity(1); // 我們連續(xù)抽 300 次獎 for (int i = 0; i < 40; i++) { System.out.println("--------第" + (i + 1) + "次抽獎----------"); // 參加抽獎,第一步點擊扣除積分 activity.debuctMoney(); // 第二步抽獎 activity.raffle(); } } } 24.1 狀態(tài)模式在實際項目-借貸平臺 源碼剖析1) 借貸平臺的訂單,有審核-發(fā)布-搶單 等等 步驟,隨著操作的不同,會改變訂單的狀態(tài), 項目中的這個模塊實現(xiàn)就會使用到狀態(tài)模式 2) 通常通過 if/else 判斷訂單的狀態(tài),從而實現(xiàn)不同的邏輯,偽代碼如下
3)代碼實現(xiàn)
package com.lin.state.project; /** * 狀態(tài)接口 * * @author Administrator * */ public interface State { /** * 電 審 */ void checkEvent(Context context); /** * 電審失敗 */ void checkFailEvent(Context context); /** * 定價發(fā)布 */ void makePriceEvent(Context context); /** * 接 單 * * */ void acceptOrderEvent(Context context); /** * 無人接單失效 */ void notPeopleAcceptEvent(Context context); /** * 付 款 */ void payOrderEvent(Context context); /** * 接單有人支付失效 */ void orderFailureEvent(Context context); /** * 反 饋 */ void feedBackEvent(Context context); String getCurrentState(); }
package com.lin.state.project; /** * 狀態(tài)枚舉類 * * @author Administrator * */ public enum StateEnum { //訂單生成 GENERATE(1, "GENERATE"), //已審核 REVIEWED(2, "REVIEWED"), //已發(fā)布 PUBLISHED(3, "PUBLISHED"), //待付款 NOT_PAY(4, "NOT_PAY"), //已付款 PAID(5, "PAID"), //已完結 FEED_BACKED(6, "FEED_BACKED"); private int key; private String value; StateEnum(int key, String value) { this.key = key; this.value = value; } public int getKey() { return key; } public String getValue() { return value; } }
package com.lin.state.project; public abstract class AbstractState implements State { protected static final RuntimeException EXCEPTION = new RuntimeException("操作流程不允許"); //抽象類,默認實現(xiàn)了 State 接口的所有方法 //該類的所有方法,其子類(具體的狀態(tài)類),可以有選擇的進行重寫 @Override public void checkEvent(Context context) { throw EXCEPTION; } @Override public void checkFailEvent(Context context) { throw EXCEPTION; } @Override public void makePriceEvent(Context context) { throw EXCEPTION; } @Override public void acceptOrderEvent(Context context) { throw EXCEPTION; } @Override public void notPeopleAcceptEvent(Context context) { throw EXCEPTION; } @Override public void payOrderEvent(Context context) { throw EXCEPTION; } @Override public void orderFailureEvent(Context context) { throw EXCEPTION; } @Override public void feedBackEvent(Context context) { throw EXCEPTION; } }
package com.lin.state.project; //環(huán)境上下文 public class Context extends AbstractState { //當前的狀態(tài) state, 根據(jù)我們的業(yè)務流程處理,不停的變化 private State state; @Override public void checkEvent(Context context) { state.checkEvent(this); getCurrentState(); } @Override public void checkFailEvent(Context context) { state.checkFailEvent(this); getCurrentState(); } @Override public void makePriceEvent(Context context) { state.makePriceEvent(this); getCurrentState(); } @Override public void acceptOrderEvent(Context context) { state.acceptOrderEvent(this); getCurrentState(); } @Override public void notPeopleAcceptEvent(Context context) { state.notPeopleAcceptEvent(this); getCurrentState(); } @Override public void payOrderEvent(Context context) { state.payOrderEvent(this); getCurrentState(); } @Override public void orderFailureEvent(Context context) { state.orderFailureEvent(this); getCurrentState(); } @Override public void feedBackEvent(Context context) { state.feedBackEvent(this); getCurrentState(); } public State getState() { return state; } public void setState(State state) { this.state = state; } @Override public String getCurrentState() { System.out.println("當前狀態(tài) : " + state.getCurrentState()); return state.getCurrentState(); } }
package com.lin.state.project; //各種具體狀態(tài)類 public class FeedBackState extends AbstractState { @Override public String getCurrentState() { return StateEnum.FEED_BACKED.getValue(); } } class GenerateState extends AbstractState { @Override public void checkEvent(Context context) { context.setState(new ReviewState()); } @Override public void checkFailEvent(Context context) { context.setState(new FeedBackState()); } @Override public String getCurrentState() { return StateEnum.GENERATE.getValue(); } } class NotPayState extends AbstractState { @Override public void payOrderEvent(Context context) { context.setState(new PaidState()); } @Override public void feedBackEvent(Context context) { context.setState(new FeedBackState()); } @Override public String getCurrentState() { return StateEnum.NOT_PAY.getValue(); } } class PaidState extends AbstractState { @Override public void feedBackEvent(Context context) { context.setState(new FeedBackState()); } @Override public String getCurrentState() { return StateEnum.PAID.getValue(); } } class PublishState extends AbstractState { @Override public void acceptOrderEvent(Context context) { //把當前狀態(tài)設置為 NotPayState。。。 //至于應該變成哪個狀態(tài),有流程圖來決定 context.setState(new NotPayState()); } @Override public void notPeopleAcceptEvent(Context context) { context.setState(new FeedBackState()); } @Override public String getCurrentState() { return StateEnum.PUBLISHED.getValue(); } } class ReviewState extends AbstractState { @Override public void makePriceEvent(Context context) { context.setState(new PublishState()); } @Override public String getCurrentState() { return StateEnum.REVIEWED.getValue(); } } package com.lin.state.project; /** 測試類 */ public class Client { public static void main(String[] args) { //創(chuàng)建 context 對象 Context context = new Context(); //將當前狀態(tài)設置為 PublishState context.setState(new PublishState()); System.out.println(context.getCurrentState()); // //publish --> not pay context.acceptOrderEvent(context); // //not pay --> paid context.payOrderEvent(context); // // 失敗, 檢測失敗時,會拋出異常 // try { // context.checkFailEvent(context); // System.out.println("流程正常.."); // } catch (Exception e) { // // TODO: handle exception // System.out.println(e.getMessage()); // } } } 24.1 狀態(tài)模式的注意事項和細節(jié)1) 代碼有很強的可讀性。狀態(tài)模式將每個狀態(tài)的行為封裝到對應的一個類中 2) 方便維護。將容易產(chǎn)生問題的 if-else 語句刪除了,如果把每個狀態(tài)的行為都放到一個類中,每次調(diào)用方法時都要判斷當前是什么狀態(tài),不但會產(chǎn)出很多 if-else 語句,而且容易出錯 3) 符合“開閉原則”。容易增刪狀態(tài) 4) 會產(chǎn)生很多類。每個狀態(tài)都要一個對應的類,當狀態(tài)過多時會產(chǎn)生很多類,加大維護難度 5) 應用場景:當一個事件或者對象有很多種狀態(tài),狀態(tài)之間會相互轉(zhuǎn)換,對不同的狀態(tài)要求有不同的行為的時候, 可以考慮使用狀態(tài)模式
僅供參考,有錯誤還請指出! 有什么想法,評論區(qū)留言,互相指教指教。 覺得不錯的可以點一下右邊的推薦喲! |
|
|