日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android设计模式之——状态模式

發(fā)布時(shí)間:2023/12/13 Android 57 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android设计模式之——状态模式 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、介紹

狀態(tài)模式中的行為是由狀態(tài)來決定的,不同的狀態(tài)下有不同的行為。狀態(tài)模式和策略模式的結(jié)構(gòu)幾乎完全一樣,但它們的目的、本質(zhì)卻完全不一樣。狀態(tài)模式的行為是平行的、不可替換的,策略模式的行為是彼此獨(dú)立、可相互替換的。用一句話來表述,狀態(tài)模式把對(duì)象的行為包裝在不同的狀態(tài)對(duì)象里,每一個(gè)狀態(tài)對(duì)象都有一個(gè)共同的抽象狀態(tài)基類。狀態(tài)模式的意圖是讓一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變的時(shí)候,其行為也隨之改變。

二、定義

當(dāng)一個(gè)對(duì)象的內(nèi)在狀態(tài)改變時(shí)允許改變其行為,這個(gè)對(duì)象看起來像是改變了其類。

三、使用場(chǎng)景

(1)一個(gè)對(duì)象的行為取決于它的狀態(tài),并且它必須在運(yùn)行時(shí)根據(jù)狀態(tài)改變它的行為。
(2)代碼中包含大量與對(duì)象狀態(tài)有關(guān)的條件語句,例如,一個(gè)操作中含有龐大的多分支語句(if-else或switch-case),且這些分支依賴于該對(duì)象的狀態(tài)。

狀態(tài)模式將每一個(gè)條件分支放入一個(gè)獨(dú)立的類中,這使得你可以根據(jù)對(duì)象自身的情況將對(duì)象的狀態(tài)作為一個(gè)對(duì)象,這一對(duì)象可以不依賴與其他對(duì)象而獨(dú)立變化,這樣通過多態(tài)去除過多的、重復(fù)的if-else等分支語句。

四、狀態(tài)模式的UML類圖

UML類圖:

角色介紹:

  • Context:環(huán)境類,定義客戶感興趣的接口,維護(hù)一個(gè)State子類的實(shí)例,這個(gè)實(shí)例定義了對(duì)象的當(dāng)前狀態(tài)。

  • State:抽象狀態(tài)類或狀態(tài)接口,定義一個(gè)或者一組接口,表示該狀態(tài)下的行為。

  • ConcreteStateA、ConcreteStateB:具體狀態(tài)類,每一個(gè)具體的狀態(tài)類實(shí)現(xiàn)抽象State中定義的接口,從而達(dá)到不同狀態(tài)下的不同行為。

五、簡(jiǎn)單示例

下面我們就以電視遙控器為例來演示一下狀態(tài)模式的實(shí)現(xiàn)。我們首先將電視的狀態(tài)簡(jiǎn)單分為開機(jī)狀態(tài)和關(guān)機(jī)狀態(tài),在開機(jī)狀態(tài)下可以通過遙控器進(jìn)行頻道切換、調(diào)整音量等操作,但是,此時(shí)重復(fù)按開機(jī)鍵是無效的;而在關(guān)機(jī)狀態(tài)下,頻道切換、調(diào)整音量、關(guān)機(jī)都是無效的操作,只有按開機(jī)按鈕時(shí)才會(huì)生效。也就是說電視的內(nèi)部狀態(tài)決定了遙控器的行為。

首先是普通的實(shí)現(xiàn)方法:

public class TVController {//開機(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("電視開機(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("沒有開機(jī)");}}public void prevChannel(){if(mState ==POWER_ON){System.out.println("上一頻道");}else{System.out.println("沒有開機(jī)");}}public void turnUp(){if(mState ==POWER_ON){System.out.println("調(diào)高音量");}else{System.out.println("沒有開機(jī)");}}public void turnDown(){if(mState ==POWER_ON){System.out.println("調(diào)低音量");}else{System.out.println("沒有開機(jī)");}} }

可以看到,每次執(zhí)行通過判斷當(dāng)前狀態(tài)來進(jìn)行操作,部分的代碼重復(fù),假設(shè)狀態(tài)和功能增加,就會(huì)越來越難以維護(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),操作無結(jié)果* * */ public class PowerOffState implements TVState{@Overridepublic void nextChannel() {}@Overridepublic void prevChannel() {}@Overridepublic void turnUp() {}@Overridepublic void turnDown() {} }

開機(jī)狀態(tài):

/*** * 開機(jī)狀態(tài),操作有效* * */ public class PowerOnState implements TVState{@Overridepublic void nextChannel() {System.out.println("下一頻道");}@Overridepublic void prevChannel() {System.out.println("上一頻道");}@Overridepublic void turnUp() {System.out.println("調(diào)高音量");}@Overridepublic 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;}@Overridepublic void powerOn() {setTVState(new PowerOnState());System.out.println("開機(jī)了");}@Overridepublic 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è)置開機(jī)狀態(tài)tvController.powerOn();//下一頻道tvController.nextChannel();//調(diào)高音量tvController.turnUp();//關(guān)機(jī)tvController.powerOff();//調(diào)低音量,此時(shí)不會(huì)生效tvController.turnDown();} }

輸出結(jié)果如下:

開機(jī)了 下一頻道 調(diào)高音量 關(guān)機(jī)了

上述實(shí)現(xiàn)中,我們抽象了一個(gè)TVState接口,該接口中有操作電視的所有函數(shù),該接口有兩個(gè)實(shí)現(xiàn)類,即開機(jī)狀態(tài)(PowerOnState)和關(guān)機(jī)狀態(tài)(PowerOffState)。開機(jī)狀態(tài)下只有開機(jī)功能是無效的,也就是說在已經(jīng)開機(jī)的時(shí)候用戶在按開機(jī)鍵不會(huì)產(chǎn)生任何反應(yīng);而在關(guān)機(jī)狀態(tài)下,只有開機(jī)功能是可用的,其他功能都不會(huì)生效。同一個(gè)操作,如調(diào)高音量的turnUp函數(shù),在關(guān)機(jī)狀態(tài)下無效,在開機(jī)狀態(tài)下就會(huì)將電視的音量調(diào)高,也就是說電視內(nèi)部狀態(tài)影響了電視遙控器的行為。狀態(tài)模式將這些行為封裝到狀態(tài)類中,在進(jìn)行操作時(shí)將這些功能轉(zhuǎn)發(fā)給狀態(tài)對(duì)象,不同的狀態(tài)有不同的實(shí)現(xiàn),這樣就通過多態(tài)的形式去除了重復(fù)、雜亂的if-else語句,這也正是狀態(tài)模式的精髓所在。

六、Android實(shí)戰(zhàn)中的使用

1、登錄系統(tǒng),根據(jù)用戶是否登錄,判斷事件的處理方式。
2、Wi-Fi管理,在不同的狀態(tài)下,WiFi的掃描請(qǐng)求處理不一。

下面以登錄系統(tǒng)為例講解下狀態(tài)模式在實(shí)戰(zhàn)中的使用:

在android開發(fā)中,我們遇到登錄界面是十分常見的,而狀態(tài)設(shè)計(jì)模式在登錄界面的應(yīng)用十分廣泛,用戶在登錄狀態(tài)下和未登錄狀態(tài)下,對(duì)邏輯的操作是不一樣的。例如最常見的情況就是在玩新浪微博的時(shí)候,用戶在登錄的情況下才能完成評(píng)論和轉(zhuǎn)發(fā)微博的操作;而當(dāng)用戶處于未登錄的情況下要執(zhí)行轉(zhuǎn)發(fā)和評(píng)論微博的操作需要進(jìn)入登錄界面登錄以后才能執(zhí)行,所以面對(duì)這兩者不同的狀況,利用狀態(tài)設(shè)計(jì)模式來設(shè)計(jì)這個(gè)例子最好不過。

1、狀態(tài)基類
前面我們講過狀態(tài)設(shè)計(jì)模式的原理實(shí)則是多態(tài),在這里我們用UserState接口表示此基類,包換轉(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、用戶在登錄和未登錄兩種狀況下的實(shí)現(xiàn)類LoginState和LogoutState;代碼如下:

在LoginState.java中,用戶是可以執(zhí)行轉(zhuǎn)發(fā)和評(píng)論操作。

public class LoginState implements UserState{@Overridepublic void forword(Context context) {Toast.makeText(context, "轉(zhuǎn)發(fā)成功", Toast.LENGTH_SHORT).show();}@Overridepublic void commit(Context context) {Toast.makeText(context, "評(píng)論成功", Toast.LENGTH_SHORT).show();} }

在LogoutState.java中,用戶在未登錄的情況下不允許執(zhí)行操作,而是應(yīng)該跳轉(zhuǎn)到登錄界面執(zhí)行登錄以后才可以執(zhí)行。

public class LogoutState implements UserState{/*** 跳轉(zhuǎn)到登錄界面登錄以后才能轉(zhuǎn)發(fā)*/@Overridepublic void forword(Context context) {gotoLohinActivity(context);}/*** 跳轉(zhuǎn)到登錄界面登錄以后才能評(píng)論*/@Overridepublic 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角色,是用戶操作對(duì)象和管理對(duì)象,LoginContext委托相關(guān)的操作給狀態(tài)對(duì)象,在其中狀態(tài)的發(fā)生改變,LoginContext的行為也發(fā)生改變。LoginContext的代碼如*下:
溫馨提示:
這里我們用到單例就是為了全局只有一個(gè)LoginContext去控制用戶狀態(tài);

public class LoginContext {//用戶狀態(tài)默認(rèn)為未登錄狀態(tài)UserState state = new LogoutState();private LoginContext(){};//私有構(gòu)造函數(shù),避免外界可以通過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;@Overrideprotected 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() {@Overridepublic 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();}}});}@Overridepublic 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, "用戶名密碼不能為空", Toast.LENGTH_SHORT).show();return;}sendLogin(username, password);break;}}}

MainActivity.java,在用戶登錄成功后,點(diǎn)擊轉(zhuǎn)發(fā)和評(píng)論執(zhí)行的是登錄狀態(tài)下的操作,而當(dāng)用戶注銷時(shí),我們把LoginContext的狀態(tài)設(shè)置為未登錄狀態(tài);LoginContext.getInstance().setState(new LogoutState());此時(shí)在點(diǎn)擊轉(zhuǎn)發(fā)和評(píng)論操作時(shí)就會(huì)跳到用戶登錄界面。

public class MainActivity extends Activity {private Button btn_forward;private Button btn_commit;private Button btn_logout;@Overrideprotected 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() {@Overridepublic void onClick(View v) {//調(diào)用LoginContext里面的轉(zhuǎn)發(fā)函數(shù)LoginContext.getInstance().forward(MainActivity.this);}});//評(píng)論操作btn_commit.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {//調(diào)用LoginContext里面的轉(zhuǎn)發(fā)函數(shù)LoginContext.getInstance().commit(MainActivity.this);}});//注銷操作btn_logout.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {//設(shè)置為注銷狀態(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)來實(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ò),通過狀態(tài)模式能夠很好的消除這類”丑陋“的邏輯處理,當(dāng)然并不是任何出現(xiàn)if-else的地方都應(yīng)該通過狀態(tài)模式重構(gòu),模式的運(yùn)用一定要考慮所處的情景以及你要解決的問題,只有符合特定的場(chǎng)景才建議使用對(duì)應(yīng)的模式。

優(yōu)點(diǎn):

  • 將所有與一個(gè)特定的狀態(tài)相關(guān)的行為都放入一個(gè)狀態(tài)對(duì)象中,它提供了一個(gè)更好的方法來組織與特定狀態(tài)相關(guān)的代碼,將繁瑣的狀態(tài)判斷轉(zhuǎn)換成結(jié)構(gòu)清晰的狀態(tài)類族,在避免代碼膨脹的同時(shí)也保證了可擴(kuò)展性與可維護(hù)性。

缺點(diǎn):

  • 狀態(tài)模式的使用必然會(huì)增加系統(tǒng)類和對(duì)象的個(gè)數(shù)。

總結(jié)

以上是生活随笔為你收集整理的Android设计模式之——状态模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。