序
最近事情太多,好久沒寫博客了, 想來之前的設計模式系列還有好幾種設計模式?jīng)]說, 正好最近寫代碼用了狀態(tài)模式, 于是便有了這篇文章.
-
前言
在平時開發(fā)中,我們的某個對象可能有很多種狀態(tài),若不用狀態(tài)模式,我們平時的"殺手锏"是if判斷, 大量的if判斷, 是什么情況干什么事情.
在這種情況下,每多一種狀態(tài),將會使得代碼要做各種修改,并且要小心翼翼是考慮各個狀態(tài)不能亂掉, 以及各狀態(tài)切換的事情.
那么這種情況下, 我們就可以考慮狀態(tài)模式.
-
定義
狀態(tài)模式把所研究的對象的行為包裝在不同的狀態(tài)對象里,每一個狀態(tài)對象都屬于一個抽象狀態(tài)類的一個子類。狀態(tài)模式的意圖是讓一個對象在其內(nèi)部狀態(tài)改變的時候眶蕉,其行為也隨之改變荣堰。
-
簡單實踐-enum類實現(xiàn)
對于UI在不同狀態(tài)的改變,可以用狀態(tài)模式解決.假設一個情況:你有4個UI模式, 分別是登錄前,登錄中,登錄失敗,登錄成功.四個模式中背景圖和標題都會改變,此時不如試試枚舉(即將UI id分裝一個類):
我們可以寫成這樣
private LogState state;
public enum LogState {
LOGGING(R.color.colorPrimaryDark,
R.string.app_name),
LOGGING1(R.color.colorPrimary,
R.string.app_name),
LOGGING3(R.color.colorPrimaryDark,
R.string.app_name),
LOGGING4(R.color.colorPrimary,
R.string.app_name);
public final int backgroundID;
public final int titleID;
LogState(int backgroundID, int titleID) {
this.backgroundID = backgroundID;
this.titleID = titleID;
}
}
此時用一個變量state
,去記錄當前的狀態(tài),而更新UI部分,背景永遠是
bg.setBackgroundResource(state.backgroundID);
title.setText(getString(state.titleID));
此時狀態(tài)與UI的更新耦合度也沒有了. UI只負責設置背景和文字,而我們自己負責改變狀態(tài)即可.而實際情況中, 我們的state日會有各種操作, 我們的UI有各種模式, 都可以這樣. 不過.Google不建議這么使用, 因為枚舉編譯完占了太多內(nèi)存了.不過上面的情況還好了.
-
普通實踐-抽象出的狀態(tài)模式
這種用法有點像策略模式,即有一個抽象類,或者是接口.每個狀態(tài)有其不同的實現(xiàn). 父類通過多態(tài),通過賦值不同的子類來實現(xiàn)狀態(tài)模式的狀態(tài)切換.
舉個遙控器列子:
遙控器有開關機的狀態(tài).那我們有兩個實現(xiàn),一個開機的實現(xiàn),一個關機的實現(xiàn)類.他們都繼承一個 press接口. 完成里面的onPressed方法.
Press mControl;
class OffControl implements Press{
@Override
public void onPressed() {
Log.d(TAG, "onPressed: 開機");
}
}
class OnControl implements Press{
@Override
public void onPressed() {
Log.d(TAG, "onPressed: 關機");
}
}
即,當mControl為OnControl狀態(tài)時,按下就會關機, 當為OffControl時,按下即會開機.
顯然,實際情況下我們的遙控器會有很多功能,這樣在開關機甚至待機等各種狀態(tài)下完成不同實現(xiàn),即可很好的管理狀態(tài).什么狀態(tài)該干什么事情,而避免大量的if else
-
神級實踐-StateMachine
位于:package com.android.internal.util;
這是Android源碼中的一個分層狀態(tài)機匙铡,非常強大, 可惜沒有向外開放, 不在AndroidSDK里面
其可以處理各種State類的轉(zhuǎn)化蒋搜。State狀態(tài)類必須實現(xiàn)processMessage方法,為了創(chuàng)建/摧毀工作環(huán)境所灸,還可以繼承實現(xiàn)enter/exit等方法荧琼。
StateMachine可以在每一個狀態(tài)內(nèi)譬胎,定義其接收不同的指令,會切換到哪個狀態(tài)命锄,而不需要狀態(tài)機主動去設定狀態(tài)堰乔,降低了主體和狀態(tài)之間的耦合,增加一個新狀態(tài)時更加方便脐恩。
其主要方法有:
addState(State state) //增加狀態(tài)
addState(State state, State parent) //增加狀態(tài), 并且告訴狀態(tài)機后者為父狀態(tài)
setInitialState(State initialState) //設置初始狀態(tài)
sendMessage(int what) //發(fā)送消息,消息由子狀態(tài)processMsg處理,若沒有子狀態(tài)處理,則調(diào)用父狀態(tài)處理.
transitionTo(IState destState) //變換狀態(tài)
transitionTo詳解:
mP0
/ \
mP1 mS0
/ \
mS2 mS1
/ \ \
mS3 mS4 mS5 ---> 初始狀態(tài)
假設初始狀態(tài)mS5镐侯,各個父狀態(tài)同樣也是活動的,于是mP0, mP1, mS1 和mS5都是活動的驶冒。當有一個消息發(fā)出來苟翻,就會依次調(diào)用mS5,
mS1, mP1, mP0的processMessage方法(前提是都會返回false或者NOT_HANDLED)。
假設mS5的processMessage可以處理這個消息骗污,并且會調(diào)用transitionTo(mS4)將狀態(tài)轉(zhuǎn)為mS4崇猫,然后返回true 或 HANDLED。processMessage返回后會進入performTransitions方法需忿,其會找到mS5和mS4的共同父狀態(tài)诅炉,也就是mP1蜡歹。緊接著會依次調(diào)用mS5.exit, mS1.exit 然后是 mS2.enter mS4.enter. 這時mP0, mP1, mS2,mS4 這四個狀態(tài)是活動的涕烧,當下一個消息到來的時候季稳,就會激活mS4.processMessage方法。
使用該狀態(tài)機可以很好的實現(xiàn)各個狀態(tài)之間的切換.
該狀態(tài)機的使用demo見 frameworks/base/core/java/com/android/internal/util/StateMachine.java
類的注釋.有興趣的可以看看.
本文作者:Anderson/Jerey_Jobs
博客地址 : http://jerey.cn/
簡書地址 : Anderson大碼渣
github地址 : https://github.com/Jerey-Jobs