您好,登錄后才能下訂單哦!
本篇文章為大家展示了Android中狀態(tài)模式的作用是什么,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。
一、介紹
狀態(tài)模式中的行為是由狀態(tài)來(lái)決定的,不同的狀態(tài)下有不同的行為。狀態(tài)模式和策略模式的結(jié)構(gòu)幾乎完全一樣,但它們的目的、本質(zhì)卻完全不一樣。狀態(tài)模式的行為是平行的、不可替換的,策略模式的行為是彼此獨(dú)立、可相互替換的。用一句話(huà)來(lái)表述,狀態(tài)模式把對(duì)象的行為包裝在不同的狀態(tài)對(duì)象里,每一個(gè)狀態(tài)對(duì)象都有一個(gè)共同的抽象狀態(tài)基類(lèi)。狀態(tài)模式的意圖是讓一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變的時(shí)候,其行為也隨之改變。
二、定義
當(dāng)一個(gè)對(duì)象的內(nèi)在狀態(tài)改變時(shí)允許改變其行為,這個(gè)對(duì)象看起來(lái)像是改變了其類(lèi)。
三、使用場(chǎng)景
(1)一個(gè)對(duì)象的行為取決于它的狀態(tài),并且它必須在運(yùn)行時(shí)根據(jù)狀態(tài)改變它的行為。
(2)代碼中包含大量與對(duì)象狀態(tài)有關(guān)的條件語(yǔ)句,例如,一個(gè)操作中含有龐大的多分支語(yǔ)句(if-else或switch-case),且這些分支依賴(lài)于該對(duì)象的狀態(tài)。
狀態(tài)模式將每一個(gè)條件分支放入一個(gè)獨(dú)立的類(lèi)中,這使得你可以根據(jù)對(duì)象自身的情況將對(duì)象的狀態(tài)作為一個(gè)對(duì)象,這一對(duì)象可以不依賴(lài)與其他對(duì)象而獨(dú)立變化,這樣通過(guò)多態(tài)去除過(guò)多的、重復(fù)的if-else等分支語(yǔ)句。
四、狀態(tài)模式的UML類(lèi)圖
UML類(lèi)圖:
角色介紹:
Context:環(huán)境類(lèi),定義客戶(hù)感興趣的接口,維護(hù)一個(gè)State子類(lèi)的實(shí)例,這個(gè)實(shí)例定義了對(duì)象的當(dāng)前狀態(tài)。
State:抽象狀態(tài)類(lèi)或狀態(tài)接口,定義一個(gè)或者一組接口,表示該狀態(tài)下的行為。
ConcreteStateA、ConcreteStateB:具體狀態(tài)類(lèi),每一個(gè)具體的狀態(tài)類(lèi)實(shí)現(xiàn)抽象State中定義的接口,從而達(dá)到不同狀態(tài)下的不同行為。
五、簡(jiǎn)單示例
下面我們就以電視遙控器為例來(lái)演示一下?tīng)顟B(tài)模式的實(shí)現(xiàn)。我們首先將電視的狀態(tài)簡(jiǎn)單分為開(kāi)機(jī)狀態(tài)和關(guān)機(jī)狀態(tài),在開(kāi)機(jī)狀態(tài)下可以通過(guò)遙控器進(jìn)行頻道切換、調(diào)整音量等操作,但是,此時(shí)重復(fù)按開(kāi)機(jī)鍵是無(wú)效的;而在關(guān)機(jī)狀態(tài)下,頻道切換、調(diào)整音量、關(guān)機(jī)都是無(wú)效的操作,只有按開(kāi)機(jī)按鈕時(shí)才會(huì)生效。也就是說(shuō)電視的內(nèi)部狀態(tài)決定了遙控器的行為。
首先是普通的實(shí)現(xiàn)方法:
public class TVController { //開(kāi)機(jī)狀態(tài) private final static int POWER_ON = 1; //關(guān)機(jī)狀態(tài) private final static int POWER_OFF = 2; //默認(rèn)狀態(tài) private int mState = POWER_OFF; public void powerOn(){ if(mState ==POWER_OFF){ System.out.println("電視開(kāi)機(jī)了"); } mState = POWER_ON; } public void powerOff(){ if(mState ==POWER_ON){ System.out.println("電視關(guān)機(jī)了"); } mState = POWER_OFF; } public void nextChannel(){ if(mState ==POWER_ON){ System.out.println("下一頻道"); }else{ System.out.println("沒(méi)有開(kāi)機(jī)"); } } public void prevChannel(){ if(mState ==POWER_ON){ System.out.println("上一頻道"); }else{ System.out.println("沒(méi)有開(kāi)機(jī)"); } } public void turnUp(){ if(mState ==POWER_ON){ System.out.println("調(diào)高音量"); }else{ System.out.println("沒(méi)有開(kāi)機(jī)"); } } public void turnDown(){ if(mState ==POWER_ON){ System.out.println("調(diào)低音量"); }else{ System.out.println("沒(méi)有開(kāi)機(jī)"); } } }
可以看到,每次執(zhí)行通過(guò)判斷當(dāng)前狀態(tài)來(lái)進(jìn)行操作,部分的代碼重復(fù),假設(shè)狀態(tài)和功能增加,就會(huì)越來(lái)越難以維護(hù)。這時(shí)可以使用狀態(tài)模式,如下:
電視的狀態(tài)接口:
/** * 電視狀態(tài)接口,定義了電視的操作函數(shù) * **/ public interface TVState { public void nextChannel(); public void prevChannel(); public void turnUp(); public void turnDown(); }
關(guān)機(jī)狀態(tài):
/** * * 關(guān)機(jī)狀態(tài),操作無(wú)結(jié)果 * * */ public class PowerOffState implements TVState{ @Override public void nextChannel() { } @Override public void prevChannel() { } @Override public void turnUp() { } @Override public void turnDown() { } }
開(kāi)機(jī)狀態(tài):
/** * * 開(kāi)機(jī)狀態(tài),操作有效 * * */ public class PowerOnState implements TVState{ @Override public void nextChannel() { System.out.println("下一頻道"); } @Override public void prevChannel() { System.out.println("上一頻道"); } @Override public void turnUp() { System.out.println("調(diào)高音量"); } @Override public void turnDown() { System.out.println("調(diào)低音量"); } }
電源操作接口:
/** * 電源操作接口 * * */ public interface PowerController { public void powerOn(); public void powerOff(); }
電視遙控器:
/** * 電視遙控器 * * */ public class TVController implements PowerController{ TVState mTVState; public void setTVState(TVState mTVState){ this.mTVState = mTVState; } @Override public void powerOn() { setTVState(new PowerOnState()); System.out.println("開(kāi)機(jī)了"); } @Override public void powerOff() { setTVState(new PowerOffState()); System.out.println("關(guān)機(jī)了"); } public void nextChannel(){ mTVState.nextChannel(); } public void prevChannel(){ mTVState.prevChannel(); } public void turnUp(){ mTVState.turnUp(); } public void turnDown(){ mTVState.turnDown(); } }
調(diào)用:
public class Client { public static void main(String[] args) { TVController tvController = new TVController(); //設(shè)置開(kāi)機(jī)狀態(tài) tvController.powerOn(); //下一頻道 tvController.nextChannel(); //調(diào)高音量 tvController.turnUp(); //關(guān)機(jī) tvController.powerOff(); //調(diào)低音量,此時(shí)不會(huì)生效 tvController.turnDown(); } }
輸出結(jié)果如下:
開(kāi)機(jī)了 下一頻道 調(diào)高音量 關(guān)機(jī)了
上述實(shí)現(xiàn)中,我們抽象了一個(gè)TVState接口,該接口中有操作電視的所有函數(shù),該接口有兩個(gè)實(shí)現(xiàn)類(lèi),即開(kāi)機(jī)狀態(tài)(PowerOnState)和關(guān)機(jī)狀態(tài)(PowerOffState)。開(kāi)機(jī)狀態(tài)下只有開(kāi)機(jī)功能是無(wú)效的,也就是說(shuō)在已經(jīng)開(kāi)機(jī)的時(shí)候用戶(hù)在按開(kāi)機(jī)鍵不會(huì)產(chǎn)生任何反應(yīng);而在關(guān)機(jī)狀態(tài)下,只有開(kāi)機(jī)功能是可用的,其他功能都不會(huì)生效。同一個(gè)操作,如調(diào)高音量的turnUp函數(shù),在關(guān)機(jī)狀態(tài)下無(wú)效,在開(kāi)機(jī)狀態(tài)下就會(huì)將電視的音量調(diào)高,也就是說(shuō)電視內(nèi)部狀態(tài)影響了電視遙控器的行為。狀態(tài)模式將這些行為封裝到狀態(tài)類(lèi)中,在進(jìn)行操作時(shí)將這些功能轉(zhuǎn)發(fā)給狀態(tài)對(duì)象,不同的狀態(tài)有不同的實(shí)現(xiàn),這樣就通過(guò)多態(tài)的形式去除了重復(fù)、雜亂的if-else語(yǔ)句,這也正是狀態(tài)模式的精髓所在。
六、Android實(shí)戰(zhàn)中的使用
1、登錄系統(tǒng),根據(jù)用戶(hù)是否登錄,判斷事件的處理方式。
2、Wi-Fi管理,在不同的狀態(tài)下,WiFi的掃描請(qǐng)求處理不一。
下面以登錄系統(tǒng)為例講解下?tīng)顟B(tài)模式在實(shí)戰(zhàn)中的使用:
在android開(kāi)發(fā)中,我們遇到登錄界面是十分常見(jiàn)的,而狀態(tài)設(shè)計(jì)模式在登錄界面的應(yīng)用十分廣泛,用戶(hù)在登錄狀態(tài)下和未登錄狀態(tài)下,對(duì)邏輯的操作是不一樣的。例如最常見(jiàn)的情況就是在玩新浪微博的時(shí)候,用戶(hù)在登錄的情況下才能完成評(píng)論和轉(zhuǎn)發(fā)微博的操作;而當(dāng)用戶(hù)處于未登錄的情況下要執(zhí)行轉(zhuǎn)發(fā)和評(píng)論微博的操作需要進(jìn)入登錄界面登錄以后才能執(zhí)行,所以面對(duì)這兩者不同的狀況,利用狀態(tài)設(shè)計(jì)模式來(lái)設(shè)計(jì)這個(gè)例子最好不過(guò)。
1、狀態(tài)基類(lèi)
前面我們講過(guò)狀態(tài)設(shè)計(jì)模式的原理實(shí)則是多態(tài),在這里我們用UserState接口表示此基類(lèi),包換轉(zhuǎn)發(fā)操作和評(píng)論這兩種狀態(tài),代碼如下:
public interface UserState { /** * 轉(zhuǎn)發(fā)操作 * @param context */ public void forword(Context context); /** * 評(píng)論操作 * @param context */ public void commit(Context context); }
2、用戶(hù)在登錄和未登錄兩種狀況下的實(shí)現(xiàn)類(lèi)LoginState和LogoutState;代碼如下:
在LoginState.java中,用戶(hù)是可以執(zhí)行轉(zhuǎn)發(fā)和評(píng)論操作。
public class LoginState implements UserState{ @Override public void forword(Context context) { Toast.makeText(context, "轉(zhuǎn)發(fā)成功", Toast.LENGTH_SHORT).show(); } @Override public void commit(Context context) { Toast.makeText(context, "評(píng)論成功", Toast.LENGTH_SHORT).show(); } }
在LogoutState.java中,用戶(hù)在未登錄的情況下不允許執(zhí)行操作,而是應(yīng)該跳轉(zhuǎn)到登錄界面執(zhí)行登錄以后才可以執(zhí)行。
public class LogoutState implements UserState{ /** * 跳轉(zhuǎn)到登錄界面登錄以后才能轉(zhuǎn)發(fā) */ @Override public void forword(Context context) { gotoLohinActivity(context); } /** * 跳轉(zhuǎn)到登錄界面登錄以后才能評(píng)論 */ @Override public void commit(Context context) { gotoLohinActivity(context); } /** * 界面跳轉(zhuǎn)操作 * @param context */ private void gotoLohinActivity(Context context){ context.startActivity(new Intent(context,LoginActivity.class)); } }
3、操作角色LoginContext
這里的LoginContext就是在狀態(tài)模式的Context角色,是用戶(hù)操作對(duì)象和管理對(duì)象,LoginContext委托相關(guān)的操作給狀態(tài)對(duì)象,在其中狀態(tài)的發(fā)生改變,LoginContext的行為也發(fā)生改變。LoginContext的代碼如*下:
溫馨提示:
這里我們用到單例就是為了全局只有一個(gè)LoginContext去控制用戶(hù)狀態(tài);
public class LoginContext { //用戶(hù)狀態(tài)默認(rèn)為未登錄狀態(tài) UserState state = new LogoutState(); private LoginContext(){};//私有構(gòu)造函數(shù),避免外界可以通過(guò)new 獲取對(duì)象 //單例模式 public static LoginContext getInstance(){ return SingletonHolder.instance; } /** *靜態(tài)代碼塊 */ private static class SingletonHolder{ private static final LoginContext instance = new LoginContext(); } public void setState(UserState state){ this.state = state; } //轉(zhuǎn)發(fā) public void forward(Context context){ state.forword(context); } //評(píng)論 public void commit(Context context){ state.commit(context); } }
4、界面展示
LoginActivity.java,此界面執(zhí)行登錄操作,登錄成后把 LoginContext.getInstance().setState(new LoginState());
設(shè)置為登錄狀態(tài),在MainActivity中就執(zhí)行的是登錄狀態(tài)下的操作,即可以轉(zhuǎn)發(fā)可評(píng)論;
public class LoginActivity extends Activity implements OnClickListener{ private static final String LOGIN_URL = "http://10.10.200.193:8080/Day01/servlet/LoginServlet"; private EditText et_username; private EditText et_password; private Button btn_login; private String username; private String password; private KJHttp http; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); initView(); initData(); } private void initView() { et_username = (EditText) findViewById(R.id.et_username); et_password = (EditText) findViewById(R.id.et_password); btn_login = (Button) findViewById(R.id.btn_login); btn_login.setOnClickListener(LoginActivity.this); } private void initData() { http = new KJHttp(); } /** * 執(zhí)行登錄操作 * * @param username2 * @param password2 */ protected void sendLogin(String username2, String password2) { HttpParams params = new HttpParams(); params.put("username", "user1"); params.put("password", "123456"); http.post(LOGIN_URL, params, new HttpCallBack() { @Override public void onSuccess(String t) { if ("200".equals(t)) { //設(shè)置為登錄狀態(tài) LoginContext.getInstance().setState(new LoginState()); startActivity(new Intent(LoginActivity.this,MainActivity.class)); finish(); Toast.makeText(LoginActivity.this, "登錄成功", Toast.LENGTH_SHORT).show(); } } }); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_login: username = et_username.getEditableText().toString().trim(); password = et_password.getEditableText().toString().trim(); if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) { Toast.makeText(LoginActivity.this, "用戶(hù)名密碼不能為空", Toast.LENGTH_SHORT).show(); return; } sendLogin(username, password); break; } } }
MainActivity.java,在用戶(hù)登錄成功后,點(diǎn)擊轉(zhuǎn)發(fā)和評(píng)論執(zhí)行的是登錄狀態(tài)下的操作,而當(dāng)用戶(hù)注銷(xiāo)時(shí),我們把LoginContext的狀態(tài)設(shè)置為未登錄狀態(tài);LoginContext.getInstance().setState(new LogoutState());
此時(shí)在點(diǎn)擊轉(zhuǎn)發(fā)和評(píng)論操作時(shí)就會(huì)跳到用戶(hù)登錄界面。
public class MainActivity extends Activity { private Button btn_forward; private Button btn_commit; private Button btn_logout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initListener(); } private void initView() { btn_forward = (Button) findViewById(R.id.btn_forward); btn_commit = (Button) findViewById(R.id.btn_commit); btn_logout = (Button) findViewById(R.id.btn_logout); } private void initListener() { //轉(zhuǎn)發(fā)操作 btn_forward.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //調(diào)用LoginContext里面的轉(zhuǎn)發(fā)函數(shù) LoginContext.getInstance().forward(MainActivity.this); } }); //評(píng)論操作 btn_commit.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //調(diào)用LoginContext里面的轉(zhuǎn)發(fā)函數(shù) LoginContext.getInstance().commit(MainActivity.this); } }); //注銷(xiāo)操作 btn_logout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //設(shè)置為注銷(xiāo)狀態(tài) LoginContext.getInstance().setState(new LogoutState()); } }); } }
七、總結(jié)
狀態(tài)模式的關(guān)鍵點(diǎn)在于不同的狀態(tài)下對(duì)于同一行為有不同的響應(yīng),這其實(shí)就是一個(gè)將if-else用多態(tài)來(lái)實(shí)現(xiàn)的一個(gè)具體示例。在if-else或者switch-case形式下根據(jù)不同的狀態(tài)進(jìn)行判斷,如果是狀態(tài)A那么執(zhí)行方法A,狀態(tài)B執(zhí)行方法B,但這種實(shí)現(xiàn)使得邏輯耦合在一起,易于出錯(cuò),通過(guò)狀態(tài)模式能夠很好的消除這類(lèi)”丑陋“的邏輯處理,當(dāng)然并不是任何出現(xiàn)if-else的地方都應(yīng)該通過(guò)狀態(tài)模式重構(gòu),模式的運(yùn)用一定要考慮所處的情景以及你要解決的問(wèn)題,只有符合特定的場(chǎng)景才建議使用對(duì)應(yīng)的模式。
優(yōu)點(diǎn):
將所有與一個(gè)特定的狀態(tài)相關(guān)的行為都放入一個(gè)狀態(tài)對(duì)象中,它提供了一個(gè)更好的方法來(lái)組織與特定狀態(tài)相關(guān)的代碼,將繁瑣的狀態(tài)判斷轉(zhuǎn)換成結(jié)構(gòu)清晰的狀態(tài)類(lèi)族,在避免代碼膨脹的同時(shí)也保證了可擴(kuò)展性與可維護(hù)性。
缺點(diǎn):
狀態(tài)模式的使用必然會(huì)增加系統(tǒng)類(lèi)和對(duì)象的個(gè)數(shù)。
上述內(nèi)容就是Android中狀態(tài)模式的作用是什么,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。