Java設(shè)計(jì)模式之狀態(tài)模式State Pattern詳解
概述
狀態(tài)模式允許對(duì)象在內(nèi)部狀態(tài)改變時(shí)改變它的行為,對(duì)象看起來好像修改了它的類。這個(gè)模式將狀態(tài)封裝成獨(dú)立的類,并將動(dòng)作委托到 代表當(dāng)前狀態(tài)的對(duì)象,我們知道行為會(huì)隨著內(nèi)部狀態(tài)而改變。
一個(gè)對(duì)象“看起來好像修改了它的類”是什么意思呢?從客戶的視角來看:如果說你使用的對(duì)象能夠完全改變它的行為,那么你會(huì)覺得,這個(gè)對(duì)象實(shí)際上是從別的類實(shí)例化而來的。然而,實(shí)際上,你知道我們是在使用組合通過簡(jiǎn)單引用不同的狀態(tài)對(duì)象來造成類改變的假象。
狀態(tài)模式它主要用來解決對(duì)象在多種狀態(tài)轉(zhuǎn)換時(shí),需要對(duì)外輸出不同的行為的問題。狀態(tài)和行為是一一對(duì)應(yīng)的,狀態(tài)之間可以相互轉(zhuǎn)換。
UML類圖
- Context是一個(gè)類,它可以擁有一些內(nèi)部狀態(tài)。
- State接口定義了一個(gè)所有具體狀態(tài)的共同接口,任何狀態(tài)都實(shí)現(xiàn)這個(gè)相同的接口,這樣一來狀態(tài)之間可以互相替換。
- 不管在什么時(shí)候,只要有人調(diào)用Context的request()方法,它就會(huì)被委托到狀態(tài)來處理。
- ConcreteState處理來自Context的請(qǐng)求。每一個(gè)ConcreteState都提供了它自己對(duì)于請(qǐng)求的實(shí)現(xiàn)。所以,當(dāng)Context改變狀態(tài)時(shí),行為也跟著改變。
狀態(tài)模式與策略模式
以狀態(tài)模式而言,我們將一群行為封裝在狀態(tài)對(duì)象中,context的行為隨時(shí)可委托到那些狀態(tài)對(duì)象中的一個(gè)。隨著時(shí)間的流逝,當(dāng)前狀態(tài)在狀態(tài)對(duì)象集合中游走改變,以反映出context內(nèi)部的狀態(tài),因此context的行為也會(huì)隨著改變。但是context的客戶對(duì)于狀態(tài)對(duì)象了解不多,甚至根本是渾然不覺。
而以策略模式而言,客戶通常主動(dòng)指定Context所要組合的策略對(duì)象是哪一個(gè)?,F(xiàn)在,固然策略模式讓我們具有彈性,能夠在運(yùn)行時(shí)改變策略,但對(duì)于某個(gè)context對(duì)象來說,通常都只有一個(gè)最適當(dāng)?shù)牟呗詫?duì)象。
一般來說,我們把策略模式想成是除了繼承之外的一種彈性替代方案。如果你使用繼承定義了一個(gè)類的行為,你將被這個(gè)行為困住,甚至要修改它都很難。有了策略模式,你可以通過組合不同的對(duì)象來改變行為。
我們把狀態(tài)模式想成是不用在context中放置許多條件判斷的替代方案。通過將行為包裝進(jìn)狀態(tài)對(duì)象中,你可以通過在context內(nèi)見到地改變狀態(tài)對(duì)象來改變context的行為。
誰(shuí)決定狀態(tài)轉(zhuǎn)換的流向
Context或者ConcreteState都可以。
一般來講,當(dāng)狀態(tài)轉(zhuǎn)換是固定的時(shí)候,就適合放在Context中。然而,當(dāng)轉(zhuǎn)換是動(dòng)態(tài)的時(shí)候,通常就會(huì)放在狀態(tài)類中。
將狀態(tài)放在狀態(tài)類中的缺點(diǎn)是:狀態(tài)類之間產(chǎn)生了依賴。
State是接口還是抽象類
答:都可以。如果我們沒有共同的功能可以放進(jìn)抽象類中,就會(huì)使用接口。在你實(shí)現(xiàn)狀態(tài)模式時(shí),很可能想使用抽象類。這么一來,當(dāng)你以后需要在抽象類中加入新的方法是就很容易,不需要打破具體狀態(tài)的實(shí)現(xiàn)。
應(yīng)用案例分析
請(qǐng)編寫程序完成APP 抽獎(jiǎng)活動(dòng)具體要求如下:
- 假如每參加一次這個(gè)活動(dòng)要扣除用戶50 積分,中獎(jiǎng)概率是10%
- 獎(jiǎng)品數(shù)量固定,抽完就不能抽獎(jiǎng)
- 活動(dòng)有四個(gè)狀態(tài): 可以抽獎(jiǎng)、不能抽獎(jiǎng)、發(fā)放獎(jiǎng)品和獎(jiǎng)品領(lǐng)完
- 活動(dòng)的四個(gè)狀態(tài)轉(zhuǎn)換關(guān)系圖
那么需要如何做呢?
- 定義出一個(gè)接口(或抽象類)叫狀態(tài)接口,每個(gè)狀態(tài)都實(shí)現(xiàn)(繼承)它。
- 接口有扣除積分方法、抽獎(jiǎng)方法、發(fā)放獎(jiǎng)品方法
狀態(tài)抽象類
/** * 狀態(tài)抽象類 */ public abstract class State { // 扣除積分 - 50 public abstract void deductMoney(); // 是否抽中獎(jiǎng)品 public abstract boolean raffle(); // 發(fā)放獎(jiǎng)品 public abstract void dispensePrize(); }
可以抽獎(jiǎng)的狀態(tài)
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)扣取過了積分"); } //可以抽獎(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("很遺憾沒有抽中獎(jiǎng)品!"); // 改變狀態(tài)為不能抽獎(jiǎng) activity.setState(activity.getNoRafflleState()); return false; } } // 不能發(fā)放獎(jiǎng)品 @Override public void dispensePrize() { System.out.println("沒中獎(jiǎng),不能發(fā)放獎(jiǎng)品"); } }
獎(jiǎng)品發(fā)放完畢狀態(tài)
/** * 獎(jiǎng)品發(fā)放完畢狀態(tài) * 說明,當(dāng)我們activity 改變成 DispenseOutState, 抽獎(jiǎng)活動(dòng)結(jié)束 */ 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)下次再參加"); } }
發(fā)放獎(jiǎng)品的狀態(tài)
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); } } }
不能抽獎(jiǎng)狀態(tài)
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)品"); } }
抽獎(jiǎng)活動(dòng)(Context)
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; } }
測(cè)試狀態(tài)模式
public class ClientTest { public static void main(String[] args) { // TODO Auto-generated method stub // 創(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 < 30; i++) { System.out.println("--------第" + (i + 1) + "次抽獎(jiǎng)----------"); // 參加抽獎(jiǎng),第一步點(diǎn)擊扣除積分 activity.debuctMoney(); // 第二步抽獎(jiǎng) activity.raffle(); } } }
UML類圖如下:
到此這篇關(guān)于Java設(shè)計(jì)模式之狀態(tài)模式State Pattern詳解的文章就介紹到這了,更多相關(guān)Java狀態(tài)模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java設(shè)計(jì)模式之狀態(tài)模式
- 深入理解Java設(shè)計(jì)模式之狀態(tài)模式
- Java設(shè)計(jì)模式之java狀態(tài)模式詳解
- 詳解JAVA 設(shè)計(jì)模式之狀態(tài)模式
- java 設(shè)計(jì)模式之State(狀態(tài)模式)
- 對(duì)比Java設(shè)計(jì)模式編程中的狀態(tài)模式和策略模式
- Java設(shè)計(jì)模式之狀態(tài)模式(State模式)介紹
- Java狀態(tài)設(shè)計(jì)模式實(shí)現(xiàn)對(duì)象狀態(tài)轉(zhuǎn)換的優(yōu)雅方式
相關(guān)文章
Java中枚舉的實(shí)現(xiàn)與應(yīng)用詳解
這篇文章主要介紹了Java中枚舉的實(shí)現(xiàn)與應(yīng)用詳解,EnumTest中還有一個(gè)VALUES數(shù)組,里面存儲(chǔ)著所有的枚舉實(shí)例,調(diào)用values方法時(shí)返回VALUES數(shù)組的clone,需要的朋友可以參考下2023-12-12Java中的Web MVC簡(jiǎn)介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
MVC模型是一種架構(gòu)型的模式,本身不引入新功能,只是幫助我們將開發(fā)的結(jié)構(gòu)組織的更加合理,使展示與模型分離、流程控制邏輯、業(yè)務(wù)邏輯調(diào)用與展示邏輯分離2017-09-09Java Lambda表達(dá)式和函數(shù)式接口實(shí)例分析
這篇文章主要介紹了Java Lambda表達(dá)式和函數(shù)式接口,結(jié)合實(shí)例形式分析了Java8 Lambda表達(dá)式和函數(shù)式接口相關(guān)原理、用法及操作注意事項(xiàng),需要的朋友可以參考下2019-09-09java實(shí)現(xiàn)輕量型http代理服務(wù)器示例
這篇文章主要介紹了java實(shí)現(xiàn)輕量型http代理服務(wù)器示例,需要的朋友可以參考下2014-04-04Java面試題沖刺第六天--網(wǎng)絡(luò)編程1
這篇文章主要為大家分享了最有價(jià)值的三道網(wǎng)絡(luò)編程面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下2021-07-07