Java設(shè)計模式之狀態(tài)模式State Pattern詳解
概述
狀態(tài)模式允許對象在內(nèi)部狀態(tài)改變時改變它的行為,對象看起來好像修改了它的類。這個模式將狀態(tài)封裝成獨(dú)立的類,并將動作委托到 代表當(dāng)前狀態(tài)的對象,我們知道行為會隨著內(nèi)部狀態(tài)而改變。
一個對象“看起來好像修改了它的類”是什么意思呢?從客戶的視角來看:如果說你使用的對象能夠完全改變它的行為,那么你會覺得,這個對象實際上是從別的類實例化而來的。然而,實際上,你知道我們是在使用組合通過簡單引用不同的狀態(tài)對象來造成類改變的假象。
狀態(tài)模式它主要用來解決對象在多種狀態(tài)轉(zhuǎn)換時,需要對外輸出不同的行為的問題。狀態(tài)和行為是一一對應(yīng)的,狀態(tài)之間可以相互轉(zhuǎn)換。
UML類圖

- Context是一個類,它可以擁有一些內(nèi)部狀態(tài)。
- State接口定義了一個所有具體狀態(tài)的共同接口,任何狀態(tài)都實現(xiàn)這個相同的接口,這樣一來狀態(tài)之間可以互相替換。
- 不管在什么時候,只要有人調(diào)用Context的request()方法,它就會被委托到狀態(tài)來處理。
- ConcreteState處理來自Context的請求。每一個ConcreteState都提供了它自己對于請求的實現(xiàn)。所以,當(dāng)Context改變狀態(tài)時,行為也跟著改變。
狀態(tài)模式與策略模式
以狀態(tài)模式而言,我們將一群行為封裝在狀態(tài)對象中,context的行為隨時可委托到那些狀態(tài)對象中的一個。隨著時間的流逝,當(dāng)前狀態(tài)在狀態(tài)對象集合中游走改變,以反映出context內(nèi)部的狀態(tài),因此context的行為也會隨著改變。但是context的客戶對于狀態(tài)對象了解不多,甚至根本是渾然不覺。
而以策略模式而言,客戶通常主動指定Context所要組合的策略對象是哪一個?,F(xiàn)在,固然策略模式讓我們具有彈性,能夠在運(yùn)行時改變策略,但對于某個context對象來說,通常都只有一個最適當(dāng)?shù)牟呗詫ο蟆?/p>
一般來說,我們把策略模式想成是除了繼承之外的一種彈性替代方案。如果你使用繼承定義了一個類的行為,你將被這個行為困住,甚至要修改它都很難。有了策略模式,你可以通過組合不同的對象來改變行為。
我們把狀態(tài)模式想成是不用在context中放置許多條件判斷的替代方案。通過將行為包裝進(jìn)狀態(tài)對象中,你可以通過在context內(nèi)見到地改變狀態(tài)對象來改變context的行為。
誰決定狀態(tài)轉(zhuǎn)換的流向
Context或者ConcreteState都可以。
一般來講,當(dāng)狀態(tài)轉(zhuǎn)換是固定的時候,就適合放在Context中。然而,當(dāng)轉(zhuǎn)換是動態(tài)的時候,通常就會放在狀態(tài)類中。
將狀態(tài)放在狀態(tài)類中的缺點(diǎn)是:狀態(tài)類之間產(chǎn)生了依賴。
State是接口還是抽象類
答:都可以。如果我們沒有共同的功能可以放進(jìn)抽象類中,就會使用接口。在你實現(xiàn)狀態(tài)模式時,很可能想使用抽象類。這么一來,當(dāng)你以后需要在抽象類中加入新的方法是就很容易,不需要打破具體狀態(tài)的實現(xiàn)。
應(yīng)用案例分析
請編寫程序完成APP 抽獎活動具體要求如下:
- 假如每參加一次這個活動要扣除用戶50 積分,中獎概率是10%
- 獎品數(shù)量固定,抽完就不能抽獎
- 活動有四個狀態(tài): 可以抽獎、不能抽獎、發(fā)放獎品和獎品領(lǐng)完
- 活動的四個狀態(tài)轉(zhuǎn)換關(guān)系圖

那么需要如何做呢?
- 定義出一個接口(或抽象類)叫狀態(tài)接口,每個狀態(tài)都實現(xiàn)(繼承)它。
- 接口有扣除積分方法、抽獎方法、發(fā)放獎品方法
狀態(tài)抽象類
/**
* 狀態(tài)抽象類
*/
public abstract class State {
// 扣除積分 - 50
public abstract void deductMoney();
// 是否抽中獎品
public abstract boolean raffle();
// 發(fā)放獎品
public abstract void dispensePrize();
}可以抽獎的狀態(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)扣取過了積分");
}
//可以抽獎, 抽完獎后,根據(jù)實際情況,改成新的狀態(tài)
@Override
public boolean raffle() {
System.out.println("正在抽獎,請稍等!");
Random r = new Random();
int num = r.nextInt(10);
// 10%中獎機(jī)會
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ā)放獎品");
}
}獎品發(fā)放完畢狀態(tài)
/**
* 獎品發(fā)放完畢狀態(tài)
* 說明,當(dāng)我們activity 改變成 DispenseOutState, 抽獎活動結(jié)束
*/
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ā)送完了,請下次再參加");
}
}發(fā)放獎品的狀態(tài)
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("抽獎活動結(jié)束");
//System.exit(0);
}
}
}不能抽獎狀態(tài)
public class NoRaffleState extends State {
// 初始化時傳入活動引用,扣除積分后改變其狀態(tài)
RaffleActivity activity;
public NoRaffleState(RaffleActivity activity) {
this.activity = activity;
}
// 當(dāng)前狀態(tài)可以扣積分 , 扣除后,將狀態(tài)設(shè)置成可以抽獎狀態(tài)
@Override
public void deductMoney() {
System.out.println("扣除50積分成功,您可以抽獎了");
activity.setState(activity.getCanRaffleState());
}
// 當(dāng)前狀態(tài)不能抽獎
@Override
public boolean raffle() {
System.out.println("扣了積分才能抽獎喔!");
return false;
}
// 當(dāng)前狀態(tài)不能發(fā)獎品
@Override
public void dispensePrize() {
System.out.println("不能發(fā)放獎品");
}
}抽獎活動(Context)
public class RaffleActivity {
// state 表示活動當(dāng)前的狀態(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);
//構(gòu)造器
//1. 初始化當(dāng)前的狀態(tài)為 noRafflleState(即不能抽獎的狀態(tài))
//2. 初始化獎品的數(shù)量
public RaffleActivity( int count) {
this.state = getNoRafflleState();
this.count = count;
}
//扣分, 調(diào)用當(dāng)前狀態(tài)的 deductMoney
public void debuctMoney(){
state.deductMoney();
}
//抽獎
public void raffle(){
// 如果當(dāng)前的狀態(tài)是抽獎成功
if(state.raffle()){
//領(lǐng)取獎品
state.dispensePrize();
}
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
//這里請大家注意,每領(lǐ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;
}
}測試狀態(tài)模式
public class ClientTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 創(chuàng)建活動對象,獎品有1個獎品
RaffleActivity activity = new RaffleActivity(1);
// 我們連續(xù)抽300次獎
for (int i = 0; i < 30; i++) {
System.out.println("--------第" + (i + 1) + "次抽獎----------");
// 參加抽獎,第一步點(diǎn)擊扣除積分
activity.debuctMoney();
// 第二步抽獎
activity.raffle();
}
}
}
UML類圖如下:

到此這篇關(guān)于Java設(shè)計模式之狀態(tài)模式State Pattern詳解的文章就介紹到這了,更多相關(guān)Java狀態(tài)模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中的Web MVC簡介_動力節(jié)點(diǎn)Java學(xué)院整理
MVC模型是一種架構(gòu)型的模式,本身不引入新功能,只是幫助我們將開發(fā)的結(jié)構(gòu)組織的更加合理,使展示與模型分離、流程控制邏輯、業(yè)務(wù)邏輯調(diào)用與展示邏輯分離2017-09-09
Java Lambda表達(dá)式和函數(shù)式接口實例分析
這篇文章主要介紹了Java Lambda表達(dá)式和函數(shù)式接口,結(jié)合實例形式分析了Java8 Lambda表達(dá)式和函數(shù)式接口相關(guān)原理、用法及操作注意事項,需要的朋友可以參考下2019-09-09
java實現(xiàn)輕量型http代理服務(wù)器示例
這篇文章主要介紹了java實現(xiàn)輕量型http代理服務(wù)器示例,需要的朋友可以參考下2014-04-04
Java面試題沖刺第六天--網(wǎng)絡(luò)編程1
這篇文章主要為大家分享了最有價值的三道網(wǎng)絡(luò)編程面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下2021-07-07

