溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Android中狀態(tài)模式的作用是什么

發(fā)布時(shí)間:2021-06-28 17:56:41 來(lái)源:億速云 閱讀:150 作者:Leah 欄目:移動(dòng)開(kāi)發(fā)

本篇文章為大家展示了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)圖:

Android中狀態(tà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è)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI