在日常開發(fā)過程中時常需要用到設(shè)計模式市怎,但是設(shè)計模式有23種拧篮,如何將這些設(shè)計模式了然于胸并且能在實際開發(fā)過程中應(yīng)用得得心應(yīng)手呢隐岛?和我一起跟著《Android源碼設(shè)計模式解析與實戰(zhàn)》一書邊學(xué)邊應(yīng)用吧!
設(shè)計模式系列文章
- Android 設(shè)計模式之單例模式
- Android 設(shè)計模式之Builder模式
- Android 設(shè)計模式之觀察者模式
- Android 設(shè)計模式之代理模式
- Android 設(shè)計模式之裝飾模式
- Android 設(shè)計模式之外觀模式
- Android 設(shè)計模式之原型模式
- Android 設(shè)計模式之工廠方法模式
- Android 設(shè)計模式之策略模式
- Android 設(shè)計模式之適配器模式
- Android 設(shè)計模式之橋接模式
- Android 設(shè)計模式之面向?qū)ο蟮牧笤瓌t
今天我們要講的是狀態(tài)模式(State模式)
定義
當一個對象的內(nèi)在狀態(tài)改變時允許改變其行為尖昏,這個對象看起來像是改變了其類
使用場景
- 一個對象的行為取決于它的狀態(tài)仰税,并且它必須在運行時根據(jù)狀態(tài)改變它的行為
- 代碼中包含大量與對象狀態(tài)有關(guān)的條件語句,例如抽诉,一個操作中含有龐大的多分支語句(if-else或switch-case)陨簇,且這些分支依賴于該對象的狀態(tài)
使用例子
- 最常見的應(yīng)用是用戶登錄系統(tǒng)
實現(xiàn)
3大角色
- 環(huán)境類:定義客戶端感興趣的接口,維護一個抽象狀態(tài)類的子類的實例掸鹅,這個實例定義了對象當前狀態(tài)
- 抽象狀態(tài)類或狀態(tài)接口:定義一個或一組接口塞帐,表示該狀態(tài)下的行為
- 具體狀態(tài)類:每個具體狀態(tài)類實現(xiàn)抽象狀態(tài)類中定義的接口,從而達到不同狀態(tài)下的不同行為
實現(xiàn)的要點
- 把不同狀態(tài)下的行為抽象成共同的接口到抽象狀態(tài)類中
- 根據(jù)不同的狀態(tài)巍沙,具體的狀態(tài)類實現(xiàn)抽象狀態(tài)類中的接口葵姥,從而實現(xiàn)不同的行為
- 環(huán)境類,也就是對外提供服務(wù)的類通過依賴抽象狀態(tài)類來實現(xiàn)具體的行為句携,同時也達到與具體狀態(tài)類的解耦
實現(xiàn)方式
下面我們以日常開發(fā)中的登錄功能來簡單應(yīng)用下狀態(tài)模式
- 在登錄模塊中一般我們會根據(jù)用戶是否登錄狀態(tài)而有不同的操作榔幸,這里簡單模擬2個操作,轉(zhuǎn)發(fā)和評論
public interface UserState {
/**
* 轉(zhuǎn)發(fā)
*
* @param context
*/
public void forward(Context context);
/**
* 評論
*
* @param context
*/
public void comment(Context context);
}
- 登錄狀態(tài)矮嫉,調(diào)用轉(zhuǎn)發(fā)和評論直接簡單地彈出個Toast
public class LoginedState implements UserState {
@Override
public void forward(Context context) {
Toast.makeText(context, "轉(zhuǎn)發(fā)成功", Toast.LENGTH_SHORT).show();
}
@Override
public void comment(Context context) {
Toast.makeText(context, "評論成功", Toast.LENGTH_SHORT).show();
}
}
- 未登錄狀態(tài)削咆,調(diào)用轉(zhuǎn)發(fā)和評論跳轉(zhuǎn)到登錄頁面,這里直接簡單地彈出登錄的提示
public class LogoutState implements UserState {
@Override
public void forward(Context context) {
Toast.makeText(context, "未登錄蠢笋,跳轉(zhuǎn)到登錄頁面", Toast.LENGTH_SHORT).show();
}
@Override
public void comment(Context context) {
Toast.makeText(context, "未登錄拨齐,跳轉(zhuǎn)到登錄頁面", Toast.LENGTH_SHORT).show();
}
}
- 根據(jù)不同的狀態(tài)表現(xiàn)不一樣的行為。下面我們看看對外提供服務(wù)的環(huán)境類
public class LoginContext {
/**
* 用戶狀態(tài)昨寞,默認為登錄狀態(tài)
*/
UserState userState = new LoginedState();
/**
* 單例
*/
static LoginContext loginContext = new LoginContext();
private LoginContext() {
}
public LoginContext getLoginContext() {
return loginContext;
}
public void setUserState(UserState userState) {
this.userState = userState;
}
public void forward(Context context) {
userState.forward(context);
}
public void comment(Context context) {
userState.comment(context);
}
}
- 以上的環(huán)境類的實現(xiàn)中瞻惋,默認狀態(tài)設(shè)置為登錄狀態(tài),這個可以根據(jù)實際需要來定援岩。下面我們看看怎么調(diào)用
//因為默認是登錄狀態(tài)歼狼,所以可以直接調(diào)用轉(zhuǎn)發(fā)功能
LoginContext.getLoginContext().forward(MainActivity.this)
//注銷登錄,也就是登錄狀態(tài)改為未登錄
LoginContext.getLoginContext().setUserState(new LogoutState());
//注銷登錄了以后再調(diào)用轉(zhuǎn)發(fā)或是評論功能享怀,執(zhí)行的就會是彈出登錄提示
LoginContext.getLoginContext().forward(MainActivity.this)
- 通過以上的例子我們就簡單地實現(xiàn)了狀態(tài)模式的應(yīng)用啦
通過上面的例子我們可以看出羽峰,狀態(tài)模式把對象的不同狀態(tài)下的行為封裝起來,并與對象的狀態(tài)聯(lián)系在一起。對象的狀態(tài)改變了梅屉,對象的行為也會改變值纱。在上面的例子中就是登錄的狀態(tài)LoginContext中的UserState狀態(tài)不一樣,執(zhí)行的行為履植,比如轉(zhuǎn)發(fā)功能就不一樣
狀態(tài)模式和策略模式
狀態(tài)模式和策略模式的結(jié)構(gòu)幾乎完全一樣计雌,但它們的目的、本質(zhì)卻完全不一樣玫霎。狀態(tài)模式的行為是平行的凿滤、不可替換的,策略模式的行為是彼此獨立庶近、可相互替換的翁脆。用一句話來表述,狀態(tài)模式把對象的行為包裝在不同的狀態(tài)對象里鼻种,每一個狀態(tài)對象都有一個共同的抽象狀態(tài)基類反番。狀態(tài)模式的意圖是讓一個對象在其內(nèi)部狀態(tài)發(fā)生改變的時候,其行為也隨之改變
總結(jié)
- 狀態(tài)模式的關(guān)鍵點在于不同的狀態(tài)下對于同一行為有不同的響應(yīng)叉钥,這其實就是一個將if-else用多態(tài)來實現(xiàn)的具體實例
- 狀態(tài)模式將所有與一個特定狀態(tài)相關(guān)的行為都放入一個狀態(tài)對象中罢缸,它提供了一個更好的方法來組織與特定狀態(tài)相關(guān)的代碼,將繁瑣的狀態(tài)判斷轉(zhuǎn)換成結(jié)構(gòu)清晰的狀態(tài)類族投队,在避免代碼膨脹的同時也保證了可擴展性和可維護性
歡迎關(guān)注我的微信公眾號枫疆,期待與你一起學(xué)習(xí),一起交流敷鸦,一起成長息楔!