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