Android编程设计模式之状态模式详解

Android编程设计模式之状态模式详解,第1张

概述本文实例讲述了Android编程设计模式之状态模式。分享给大家供大家参考,具体如下:

本文实例讲述了AndroID编程设计模式之状态模式。分享给大家供大家参考,具体如下:

一、介绍

状态模式中的行为是由状态来决定的,不同的状态下有不同的行为。状态模式和策略模式的结构几乎完全一样,但它们的目的、本质却完全不一样。状态模式的行为是平行的、不可替换的,策略模式的行为是彼此独立、可相互替换的。用一句话来表述,状态模式把对象的行为包装在不同的状态对象里,每一个状态对象都有一个共同的抽象状态基类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。

二、定义

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

三、使用场景

(1)一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。
(2)代码中包含大量与对象状态有关的条件语句,例如,一个 *** 作中含有庞大的多分支语句(if-else或switch-case),且这些分支依赖于该对象的状态。

状态模式将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖与其他对象而独立变化,这样通过多态去除过多的、重复的if-else等分支语句。

四、状态模式的UML类图

UML类图:

角色介绍:

Context:环境类,定义客户感兴趣的接口,维护一个State子类的实例,这个实例定义了对象的当前状态。

State:抽象状态类或状态接口,定义一个或者一组接口,表示该状态下的行为。

ConcreteStateAConcreteStateB:具体状态类,每一个具体的状态类实现抽象State中定义的接口,从而达到不同状态下的不同行为。

五、简单示例

下面我们就以电视遥控器为例来演示一下状态模式的实现。我们首先将电视的状态简单分为开机状态和关机状态,在开机状态下可以通过遥控器进行频道切换、调整音量等 *** 作,但是,此时重复按开机键是无效的;而在关机状态下,频道切换、调整音量、关机都是无效的 *** 作,只有按开机按钮时才会生效。也就是说电视的内部状态决定了遥控器的行为。

首先是普通的实现方法:

public class TVController {  //开机状态  private final static int POWER_ON = 1;  //关机状态  private final static int POWER_OFF = 2;  //默认状态  private int mState = POWER_OFF;  public voID powerOn(){    if(mState ==POWER_OFF){      System.out.println("电视开机了");    }    mState = POWER_ON;  }  public voID powerOff(){    if(mState ==POWER_ON){      System.out.println("电视关机了");    }    mState = POWER_OFF;  }  public voID nextChannel(){    if(mState ==POWER_ON){      System.out.println("下一频道");    }else{      System.out.println("没有开机");    }  }  public voID prevChannel(){    if(mState ==POWER_ON){      System.out.println("上一频道");    }else{      System.out.println("没有开机");    }  }  public voID turnUp(){    if(mState ==POWER_ON){      System.out.println("调高音量");    }else{      System.out.println("没有开机");    }  }  public voID turnDown(){    if(mState ==POWER_ON){      System.out.println("调低音量");    }else{      System.out.println("没有开机");    }  }}

可以看到,每次执行通过判断当前状态来进行 *** 作,部分的代码重复,假设状态和功能增加,就会越来越难以维护。这时可以使用状态模式,如下:

电视的状态接口:

/** * 电视状态接口,定义了电视的 *** 作函数 * **/public interface Tvstate {  public voID nextChannel();  public voID prevChannel();  public voID turnUp();  public voID turnDown();}

关机状态:

/** * * 关机状态, *** 作无结果 * * */public class PowerOffState implements Tvstate{  @OverrIDe  public voID nextChannel() {  }  @OverrIDe  public voID prevChannel() {  }  @OverrIDe  public voID turnUp() {  }  @OverrIDe  public voID turnDown() {  }}

开机状态:

/** * * 开机状态, *** 作有效 * * */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("调高音量");  }  @OverrIDe  public voID turnDown() {    System.out.println("调低音量");  }}

电源 *** 作接口:

/** * 电源 *** 作接口 * * */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("开机了");  }  @OverrIDe  public voID powerOff() {    setTvstate(new PowerOffState());    System.out.println("关机了");  }  public voID nextChannel(){    mTvstate.nextChannel();  }  public voID prevChannel(){    mTvstate.prevChannel();  }  public voID turnUp(){    mTvstate.turnUp();  }  public voID turnDown(){    mTvstate.turnDown();  }}

调用:

public class ClIEnt {  public static voID main(String[] args) {    TVController tvController = new TVController();    //设置开机状态    tvController.powerOn();    //下一频道    tvController.nextChannel();    //调高音量    tvController.turnUp();    //关机    tvController.powerOff();    //调低音量,此时不会生效    tvController.turnDown();  }}

输出结果如下:

开机了下一频道调高音量关机了

上述实现中,我们抽象了一个Tvstate接口,该接口中有 *** 作电视的所有函数,该接口有两个实现类,即开机状态(PowerOnState)和关机状态(PowerOffState)。开机状态下只有开机功能是无效的,也就是说在已经开机的时候用户在按开机键不会产生任何反应;而在关机状态下,只有开机功能是可用的,其他功能都不会生效。同一个 *** 作,如调高音量的turnUp函数,在关机状态下无效,在开机状态下就会将电视的音量调高,也就是说电视内部状态影响了电视遥控器的行为。状态模式将这些行为封装到状态类中,在进行 *** 作时将这些功能转发给状态对象,不同的状态有不同的实现,这样就通过多态的形式去除了重复、杂乱的if-else语句,这也正是状态模式的精髓所在。

六、AndroID实战中的使用

1、登录系统,根据用户是否登录,判断事件的处理方式。
2、Wi-Fi管理,在不同的状态下,WiFi的扫描请求处理不一。

下面以登录系统为例讲解下状态模式在实战中的使用:

在androID开发中,我们遇到登录界面是十分常见的,而状态设计模式在登录界面的应用十分广泛,用户在登录状态下和未登录状态下,对逻辑的 *** 作是不一样的。例如最常见的情况就是在玩新浪微博的时候,用户在登录的情况下才能完成评论和转发微博的 *** 作;而当用户处于未登录的情况下要执行转发和评论微博的 *** 作需要进入登录界面登录以后才能执行,所以面对这两者不同的状况,利用状态设计模式来设计这个例子最好不过。

1、状态基类

前面我们讲过状态设计模式的原理实则是多态,在这里我们用UserState接口表示此基类,包换转发 *** 作和评论这两种状态,代码如下:

public interface UserState {  /**   * 转发 *** 作   * @param context   */  public voID forword(Context context);  /**   * 评论 *** 作   * @param context   */  public voID commit(Context context);}

2、用户在登录和未登录两种状况下的实现类LoginState和logoutState;代码如下:

在LoginState.java中,用户是可以执行转发和评论 *** 作。

public class LoginState implements UserState{  @OverrIDe  public voID forword(Context context) {    Toast.makeText(context,"转发成功",Toast.LENGTH_SHORT).show();  }  @OverrIDe  public voID commit(Context context) {    Toast.makeText(context,"评论成功",Toast.LENGTH_SHORT).show();  }}

在logoutState.java中,用户在未登录的情况下不允许执行 *** 作,而是应该跳转到登录界面执行登录以后才可以执行。

public class logoutState implements UserState{  /**   * 跳转到登录界面登录以后才能转发   */  @OverrIDe  public voID forword(Context context) {    gotolohinActivity(context);  }  /**   * 跳转到登录界面登录以后才能评论   */  @OverrIDe  public voID commit(Context context) {    gotolohinActivity(context);  }  /**   * 界面跳转 *** 作   * @param context   */  private voID gotolohinActivity(Context context){    context.startActivity(new Intent(context,LoginActivity.class));  }}

3、 *** 作角色LoginContext

这里的LoginContext就是在状态模式的Context角色,是用户 *** 作对象和管理对象,LoginContext委托相关的 *** 作给状态对象,在其中状态的发生改变,LoginContext的行为也发生改变。LoginContext的代码如*下:

温馨提示:

这里我们用到单例就是为了全局只有一个LoginContext去控制用户状态;

public class LoginContext {  //用户状态默认为未登录状态  UserState state = new logoutState();  private LoginContext(){};//私有构造函数,避免外界可以通过new 获取对象  //单例模式  public static LoginContext getInstance(){   return SingletonHolder.instance;  }  /**  *静态代码块  */  private static class SingletonHolder{   private static final LoginContext instance = new LoginContext();  }  public voID setState(UserState state){    this.state = state;  }  //转发  public voID forward(Context context){    state.forword(context);  }  //评论  public voID commit(Context context){    state.commit(context);  }}

4、界面展示

LoginActivity.java,此界面执行登录 *** 作,登录成后把 LoginContext.getInstance().setState(new LoginState());设置为登录状态,在MainActivity中就执行的是登录状态下的 *** 作,即可以转发可评论;

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();  }  /**   * 执行登录 *** 作   *   * @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)) {          //设置为登录状态          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,"用户名密码不能为空",Toast.LENGTH_SHORT).show();        return;      }      sendLogin(username,password);      break;    }  }}

MainActivity.java,在用户登录成功后,点击转发和评论执行的是登录状态下的 *** 作,而当用户注销时,我们把LoginContext的状态设置为未登录状态;LoginContext.getInstance().setState(new logoutState());此时在点击转发和评论 *** 作时就会跳到用户登录界面。

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() {    //转发 *** 作    btn_forward.setonClickListener(new OnClickListener() {      @OverrIDe      public voID onClick(VIEw v) {        //调用LoginContext里面的转发函数        LoginContext.getInstance().forward(MainActivity.this);      }    });    //评论 *** 作    btn_commit.setonClickListener(new OnClickListener() {      @OverrIDe      public voID onClick(VIEw v) {        //调用LoginContext里面的转发函数        LoginContext.getInstance().commit(MainActivity.this);      }    });    //注销 *** 作    btn_logout.setonClickListener(new OnClickListener() {      @OverrIDe      public voID onClick(VIEw v) {        //设置为注销状态        LoginContext.getInstance().setState(new logoutState());      }    });  }}

七、总结

状态模式的关键点在于不同的状态下对于同一行为有不同的响应,这其实就是一个将if-else用多态来实现的一个具体示例。在if-else或者switch-case形式下根据不同的状态进行判断,如果是状态A那么执行方法A,状态B执行方法B,但这种实现使得逻辑耦合在一起,易于出错,通过状态模式能够很好的消除这类”丑陋“的逻辑处理,当然并不是任何出现if-else的地方都应该通过状态模式重构,模式的运用一定要考虑所处的情景以及你要解决的问题,只有符合特定的场景才建议使用对应的模式。

优点:

将所有与一个特定的状态相关的行为都放入一个状态对象中,它提供了一个更好的方法来组织与特定状态相关的代码,将繁琐的状态判断转换成结构清晰的状态类族,在避免代码膨胀的同时也保证了可扩展性与可维护性。

缺点:

状态模式的使用必然会增加系统类和对象的个数。

更多关于AndroID相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android调试技巧与常见问题解决方法汇总》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结》

希望本文所述对大家AndroID程序设计有所帮助。

总结

以上是内存溢出为你收集整理的Android编程设计模式之状态模式详解全部内容,希望文章能够帮你解决Android编程设计模式之状态模式详解所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1143289.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存